MediaProvider.java revision 3e6a42887a17a93b8906ff05693587e25158f2b1
1702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/*
2702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
3702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
4702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * you may not use this file except in compliance with the License.
6702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * You may obtain a copy of the License at
7702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
8702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
10702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * See the License for the specific language governing permissions and
14702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * limitations under the License.
15702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
16702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
17702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpackage com.android.providers.media;
18702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
19007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.ACCESS_CACHE_FILESYSTEM;
20007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.READ_EXTERNAL_STORAGE;
21007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
22f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
23007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
24007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
25702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.app.SearchManager;
26bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.BroadcastReceiver;
27bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ComponentName;
28bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProvider;
29bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderOperation;
30bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderResult;
31bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentResolver;
32bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentUris;
33bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentValues;
34bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Context;
35bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Intent;
36bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.IntentFilter;
37bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.OperationApplicationException;
38bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ServiceConnection;
39ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.content.SharedPreferences;
40bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.UriMatcher;
413e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissenimport android.content.pm.PackageManager;
4290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissenimport android.content.pm.PackageManager.NameNotFoundException;
4370676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.content.res.Resources;
44702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
45ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils;
460027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor;
47702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
48702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap;
51702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory;
52b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.media.MediaFile;
53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner;
5438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection;
5538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection.MediaScannerConnectionClient;
56b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
5790345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpConstants;
589be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwoodimport android.mtp.MtpStorage;
59702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
60702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
6138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.os.Bundle;
62702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
63702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils;
64702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler;
65ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread;
66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message;
67702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor;
68702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process;
69d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException;
7010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.os.SystemClock;
71c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwoodimport android.os.storage.StorageManager;
721f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwoodimport android.os.storage.StorageVolume;
73ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.preference.PreferenceManager;
74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns;
75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore;
76702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio;
774eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissenimport android.provider.MediaStore.Audio.Playlists;
78a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Linimport android.provider.MediaStore.Files;
7970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Files.FileColumns;
80702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images;
8170676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Images.ImageColumns;
82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns;
83702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video;
84702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
8510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.text.format.DateUtils;
86702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
87702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
88702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
8910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.FileDescriptor;
90702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
91702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
92702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
93702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
9410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.PrintWriter;
95cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList;
9610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.util.Collection;
97702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
98702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
99702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
100f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List;
10138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport java.util.Locale;
102b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
1038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
105166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissenimport libcore.io.ErrnoException;
10620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissenimport libcore.io.IoUtils;
107166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissenimport libcore.io.Libcore;
10870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissenimport libcore.io.OsConstants;
10970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissenimport libcore.io.StructStat;
110166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/**
112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details.
113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the
114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index).  The content visible at content://media/external/...
115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card.
116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider {
118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri MEDIA_URI = Uri.parse("content://media");
119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");
120b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int ALBUM_THUMB = 1;
121b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int IMAGE_THUMB = 2;
122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>();
124d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen    private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>();
125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
126007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /** Resolved canonical path to external storage. */
127007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private static final String sExternalPath;
128007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /** Resolved canonical path to cache storage. */
129007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private static final String sCachePath;
130007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
131007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    static {
132007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
133007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            sExternalPath = Environment.getExternalStorageDirectory().getCanonicalPath();
134007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            sCachePath = Environment.getDownloadCacheDirectory().getCanonicalPath();
135007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } catch (IOException e) {
136007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throw new RuntimeException("Unable to resolve canonical paths", e);
137007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
138007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
139007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
1407f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    // In memory cache of path<->id mappings, to speed up inserts during media scan
1417f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>();
1427f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
1438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
1448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
1458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
1468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
1478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
1488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
14920434e032e498b716f87cce2f23dd646819218bfRay Chen    // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest.
15020434e032e498b716f87cce2f23dd646819218bfRay Chen    private MediaThumbRequest mCurrentThumbRequest = null;
151b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
152b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
153b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
154b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
155f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood    private boolean mCaseInsensitivePaths;
1569be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private static String[] mExternalStoragePaths;
15717ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood
158a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
159a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
160a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
161a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
164a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
166a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
167a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
168a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
169a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
170a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
171a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
172a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
173a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
174a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
175a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
176ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
177ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
178a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
179a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
180a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
181a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
182a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
183a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
184a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
185a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
186a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
187a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
18863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
189a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
190a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
191a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
192a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
193a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
194a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
195a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
196a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
197a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
19863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
19963f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
200e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen            " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" +
20163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
202a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
203a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
20463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
20563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
206a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
207a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTableColumns = new String[] {
20816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            FileColumns._ID,
209afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            FileColumns.MEDIA_TYPE,
2101717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    };
2111717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
2127f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    private static final String[] sIdOnlyColumn = new String[] {
2137f36494e085c26c69cd5925e54028822025eff29Marco Nelissen        FileColumns._ID
2147f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    };
2157f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
216166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    private static final String[] sDataOnlyColumn = new String[] {
217166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        FileColumns.DATA
218166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    };
219166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
220a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTypeDataId = new String[] {
221a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.MEDIA_TYPE,
222a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.DATA,
223a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns._ID
2244eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    };
2254eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen
2264eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    private static final String[] sPlaylistIdPlayOrder = new String[] {
2274eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen        Playlists.Members.PLAYLIST_ID,
2284eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen        Playlists.Members.PLAY_ORDER
2294eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    };
230a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
231a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
232a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
2371f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                StorageVolume storage = (StorageVolume)intent.getParcelableExtra(
2381f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        StorageVolume.EXTRA_STORAGE_VOLUME);
2391f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // If primary external storage is ejected, then remove the external volume
2401f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // notify all cursors backed by data on that volume.
2411f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                if (storage.getPath().equals(mExternalStoragePaths[0])) {
2421f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    detachVolume(Uri.parse("content://media/external"));
2431f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    sFolderArtMap.clear();
2441f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    MiniThumbFile.reset();
2451f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                } else {
2461f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // If secondary external storage is ejected, then we delete all database
2471f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // entries for that storage from the files table.
2481f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    synchronized (mDatabases) {
2491f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        DatabaseHelper database = mDatabases.get(EXTERNAL_VOLUME);
2501f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        Uri uri = Uri.parse("file://" + storage.getPath());
2511f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        if (database != null) {
2521f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            try {
2531f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // Send media scanner started and stopped broadcasts for apps that rely
2541f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // on these Intents for coarse grained media database notifications.
2551f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2561f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
2571f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood
2581f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // don't send objectRemoved events - MTP be sending StorageRemoved anyway
2591f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = true;
2601f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.d(TAG, "deleting all entries for storage " + storage);
2611f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                SQLiteDatabase db = database.getWritableDatabase();
2624b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // First clear the file path to disable the _DELETE_FILE database hook.
2634b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // We do this to avoid deleting files if the volume is remounted while
2644b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // we are still processing the unmount event.
2654b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                ContentValues values = new ContentValues();
2664b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                values.put(Files.FileColumns.DATA, "");
2674b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String where = FileColumns.STORAGE_ID + "=?";
2684b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) };
2699b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                database.mNumUpdates++;
2704b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                db.update("files", values, where, whereArgs);
2714b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // now delete the records
2729b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                database.mNumDeletes++;
2739b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                int numpurged = db.delete("files", where, whereArgs);
2749b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                logToDb(db, "removed " + numpurged +
2759b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                        " rows for ejected filesystem " + storage.getPath());
2764b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // notify on media Uris as well as the files Uri
2774b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2784b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Audio.Media.getContentUri(EXTERNAL_VOLUME), null);
2794b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2804b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Images.Media.getContentUri(EXTERNAL_VOLUME), null);
2814b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2824b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Video.Media.getContentUri(EXTERNAL_VOLUME), null);
2831f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.getContentResolver().notifyChange(
2841f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        Files.getContentUri(EXTERNAL_VOLUME), null);
2851f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } catch (Exception e) {
2861f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.e(TAG, "exception deleting storage entries", e);
2871f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } finally {
2881f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2891f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
2901f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = false;
2911f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            }
2921f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        }
2931f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    }
2941f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                }
295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
299d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    // set to disable sending events when the operation originates from MTP
300d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private boolean mDisableMtpObjectCallbacks;
301d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
302d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final SQLiteDatabase.CustomFunction mObjectRemovedCallback =
303d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                new SQLiteDatabase.CustomFunction() {
304d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void callback(String[] args) {
3057f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // We could remove only the deleted entry from the cache, but that
3067f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // requires the path, which we don't have here, so instead we just
3077f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // clear the entire cache.
3087f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: include the path in the callback and only remove the affected
3097f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // entry from the cache
3107f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            mDirectoryCache.clear();
311d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // do nothing if the operation originated from MTP
312d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mDisableMtpObjectCallbacks) return;
313d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
314d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "object removed " + args[0]);
315d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            IMtpService mtpService = mMtpService;
316d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mtpService != null) {
317d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                try {
318d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    sendObjectRemoved(Integer.parseInt(args[0]));
319d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                } catch (NumberFormatException e) {
320d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e);
321d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
322d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
323d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
324d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
325d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
331fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static final class DatabaseHelper extends SQLiteOpenHelper {
332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
3335524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        final String mName;
334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
335fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final boolean mEarlyUpgrade;
336fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final SQLiteDatabase.CustomFunction mObjectRemovedCallback;
3375524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        boolean mUpgradeAttempted; // Used for upgrade error handling
33810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumQueries;
33910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumUpdates;
34010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumInserts;
34110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumDeletes;
34210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStartTime;
34310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStopTime;
344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
349fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        public DatabaseHelper(Context context, String name, boolean internal,
350fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                boolean earlyUpgrade,
351fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                SQLiteDatabase.CustomFunction objectRemovedCallback) {
35290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            super(context, name, null, getDatabaseVersion(context));
353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
3545524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mName = name;
355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
356fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mEarlyUpgrade = earlyUpgrade;
357fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mObjectRemovedCallback = objectRemovedCallback;
3580e2a2386b39972286df21f4db5a9dd1df548c34dJeff Brown            setWriteAheadLoggingEnabled(true);
359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
36690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, 0, getDatabaseVersion(mContext));
367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
3755524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = true;
37690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, oldV, newV);
377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
379db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        @Override
380db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        public synchronized SQLiteDatabase getWritableDatabase() {
3815524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            SQLiteDatabase result = null;
3825524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = false;
3835524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            try {
3845524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3855524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            } catch (Exception e) {
3865524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                if (!mUpgradeAttempted) {
3875524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    Log.e(TAG, "failed to open database " + mName, e);
3885524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    return null;
3895524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                }
3905524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3915524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
3925524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // If we failed to open the database during an upgrade, delete the file and try again.
3935524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // This will result in the creation of a fresh database, which will be repopulated
3945524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // when the media scanner runs.
3955524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            if (result == null && mUpgradeAttempted) {
396450d884f1bd5de323a645ce1acfae40fb91b8cb0jangwon.lee                mContext.deleteDatabase(mName);
3975524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3985524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3995524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            return result;
4005524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        }
4015524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
403993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * For devices that have removable storage, we support keeping multiple databases
404993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * to allow users to switch between a number of cards.
405993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * On such devices, touch this particular database and garbage collect old databases.
406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
41136d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky
412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
414fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mEarlyUpgrade) return; // Doing early upgrade.
415fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
416fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mObjectRemovedCallback != null) {
417fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback);
418fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            }
419d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
420993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            // the code below is only needed on devices with removable storage
421993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            if (!Environment.isExternalStorageRemovable()) return;
422993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
48334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood    // synchronize on mMtpServiceConnection when accessing mMtpService
484d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private IMtpService mMtpService;
485d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
486d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final ServiceConnection mMtpServiceConnection = new ServiceConnection() {
487d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood         public void onServiceConnected(ComponentName className, android.os.IBinder service) {
48834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
48934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = IMtpService.Stub.asInterface(service);
49034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
491d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
492d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
493d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void onServiceDisconnected(ComponentName className) {
49434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
49534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = null;
49634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
497d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
498d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
499d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
500ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    private static final String[] sDefaultFolderNames = {
501ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MUSIC,
502ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PODCASTS,
503ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_RINGTONES,
504ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_ALARMS,
505ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_NOTIFICATIONS,
506ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PICTURES,
507ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MOVIES,
508ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DOWNLOADS,
509ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DCIM,
510ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    };
511ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
512ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    // creates default folders (Music, Downloads, etc)
51310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void createDefaultFolders(DatabaseHelper helper, SQLiteDatabase db) {
514ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // Use a SharedPreference to ensure we only do this once.
515ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // We don't want to annoy the user by recreating the directories
516ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // after she has deleted them.
517ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
518ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        if (prefs.getInt("created_default_folders", 0) == 0) {
519ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            for (String folderName : sDefaultFolderNames) {
520ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                File file = Environment.getExternalStoragePublicDirectory(folderName);
521ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                if (!file.exists()) {
522ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                    file.mkdirs();
52310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    insertDirectory(helper, db, file.getAbsolutePath());
524ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                }
525ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            }
526ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
527ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            SharedPreferences.Editor e = prefs.edit();
528ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.clear();
529ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.putInt("created_default_folders", 1);
530ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.commit();
531ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        }
532ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
533ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
5349311c7ff9d35ca3acc908da3da7a79fbf7a8da6bMarco Nelissen    public static int getDatabaseVersion(Context context) {
53590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        try {
53690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            return context.getPackageManager().getPackageInfo(
53790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    context.getPackageName(), 0).versionCode;
53890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        } catch (NameNotFoundException e) {
53990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            throw new RuntimeException("couldn't get version code for " + context);
54090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
54190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    }
54290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen
543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
545d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        final Context context = getContext();
546d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
547acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
548acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums._ID);
549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
551acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
553acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
558acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
559acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
560acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
561acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.ALBUM_ART);
562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
56363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
56463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
565d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "%1", context.getString(R.string.artist_label));
566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
571d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        context.registerReceiver(mUnmountReceiver, iFilter);
572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
573c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        StorageManager storageManager =
574c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood                (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
575c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        mExternalStoragePaths = storageManager.getVolumePaths();
5769be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
584ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND);
585ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        ht.start();
586ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        mThumbHandler = new Handler(ht.getLooper()) {
587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
589b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
590b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
59120434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest = mMediaThumbQueue.poll();
592b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
59320434e032e498b716f87cce2f23dd646819218bfRay Chen                    if (mCurrentThumbRequest == null) {
594b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
595b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
596b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
59720434e032e498b716f87cce2f23dd646819218bfRay Chen                            File origFile = new File(mCurrentThumbRequest.mPath);
5984d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            if (origFile.exists() && origFile.length() > 0) {
59920434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.execute();
6004d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            } else {
6014d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                // original file hasn't been stored yet
6024d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                synchronized (mMediaThumbQueue) {
60320434e032e498b716f87cce2f23dd646819218bfRay Chen                                    Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath);
6044d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
6054d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
606b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
6071d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
6081d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                        } catch (UnsupportedOperationException ex) {
6091d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // This could happen if we unplug the sd card during insert/update/delete
6101d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // See getDatabaseForUri.
6111d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
61222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                        } catch (OutOfMemoryError err) {
61322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            /*
61422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Note: Catching Errors is in most cases considered
61522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * bad practice. However, in this case it is
61622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * motivated by the fact that corrupt or very large
61722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * images may cause a huge allocation to be
61822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * requested and denied. The bitmap handling API in
61922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Android offers no other way to guard against
62022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * these problems than by catching OutOfMemoryError.
62122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             */
62222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            Log.w(TAG, err);
623b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
62420434e032e498b716f87cce2f23dd646819218bfRay Chen                            synchronized (mCurrentThumbRequest) {
62520434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE;
62620434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.notifyAll();
627b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
628b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
629b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
630b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
632b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
633b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
634b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
636d63eb65db514ad7064951f221f0278a3f2293411Jeff Sharkey                    IoUtils.closeQuietly(makeThumbInternal(d));
637b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
638b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
639b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
647395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_FILES = "files";
648395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_ALBUM_ART = "album_art";
649395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_THUMBNAILS = "thumbnails";
650395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_VIDEO_THUMBNAILS = "videothumbnails";
651395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
652afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String IMAGE_COLUMNS =
653afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_size,_display_name,mime_type,title,date_added," +
654afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
655bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name," +
656bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "width,height";
657bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
658bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String IMAGE_COLUMNSv407 =
659bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_size,_display_name,mime_type,title,date_added," +
660bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
661afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name";
662afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
663805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv99 =
664afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added," +
665afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
666afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
667afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark";
668afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
669805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv100 =
670805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "_data,_display_name,_size,mime_type,date_added," +
671805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
672805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
673805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "bookmark,album_artist";
674805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen
675957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang    private static final String AUDIO_COLUMNSv405 =
676957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "_data,_display_name,_size,mime_type,date_added,is_drm," +
677957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
678957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
679957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "bookmark,album_artist";
680957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
681afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String VIDEO_COLUMNS =
682afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
683afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title,duration,artist,album,resolution,description,isprivate,tags," +
684afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
685bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width," +
686bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "height";
687bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
688bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String VIDEO_COLUMNSv407 =
689bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
690bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "title,duration,artist,album,resolution,description,isprivate,tags," +
691bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
692afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic,bucket_id,bucket_display_name, bookmark";
693afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
694afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified";
695afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
70490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    private static void updateDatabase(Context context, SQLiteDatabase db, boolean internal,
705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
70890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        int dbversion = getDatabaseVersion(context);
70990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (toVersion != dbversion) {
71090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " + dbversion);
711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
71395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
717988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        long startTime = SystemClock.currentTimeMicro();
718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
719d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag.
720acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        // We can't downgrade from those revisions, so start over.
721022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // (the initial change to do this was wrong, so now we actually need to start over
722022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // if the database version is 84-89)
723bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair.
724bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // However version 91 was reused in a divergent development path for gingerbread,
725bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // so we need to support upgrades from 91.
726bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Therefore we will only force a reset for versions 92 - 94.
727d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) ||
728bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    (fromVersion >= 92 && fromVersion <= 94)) {
729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
732d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            fromVersion = 63;
733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
755cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
7569ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup");
7579ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup");
7589ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup");
7599ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup");
760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
790b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
810216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                       "_data TEXT UNIQUE NOT NULL," +
811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
85095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
856acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album) AS number_of_albums, " +
857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
870acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
871acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
872acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
873acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
874acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
89278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                // Contains mappings between audio genres and audio files
893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
983acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
984acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.0 shipped with database version 64
985acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
986acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
992403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // In version 66, originally we updateBucketNames(db, "images"),
993403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // but we need to do it in version 89 and therefore save the update here.
994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
1002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
1003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
1004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
1005403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1006403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // In version 68, originally we updateBucketNames(db, "video"),
1007403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // but we need to do it in version 89 and therefore save the update here.
1008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
1011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
1012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
1015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
1016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
1017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
101895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
1020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
1021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
1022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
1023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
1024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
1025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
1026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
1027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
1028e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "artist='" + MediaStore.UNKNOWN_STRING + "' AND " +
1029e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "album='" + MediaStore.UNKNOWN_STRING + "'" +
1030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
1031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
103295ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
1034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
1035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
1036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
1037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
1038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
1039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
1040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
1042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
1043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
1044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
1045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
1046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
104795ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1048acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1049acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.5 shipped with database version 72
1050acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1051acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
10528d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
10538d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
10548d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
10558d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
10568d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
10578d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
10588d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
10598d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
10608d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
10618d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
10628d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
10638d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
10648d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
10658d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
10668d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
1067a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1068a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
1069a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
1070a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
1071a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
1072a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
1073a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
1074a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
1075a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1076a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
1077a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1078a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
1079a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1080a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
1081a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1082a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
1083a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
1084a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
1085a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
1086a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
1087a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
1088a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
1089e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " +
1090a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1091a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1092a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
1093a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1094a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1095a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1096a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
1097a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1098a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1099a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1100a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
1101a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
1102a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
1103e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " +
1104a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1105a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
1106a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
1107a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1108a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
1110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
1111a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1112a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1113a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1114a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
1115a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
1116a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
1117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
1118a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
1119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
1120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
112159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
112259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
112395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
112459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
112559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
112659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
112759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
112815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
112915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
113015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
113115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
113215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
113315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
113415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
113515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
113615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
113715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
113815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
113915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
114015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
114115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
1142b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1143acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1144acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.6 shipped with database version 76
1145acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1146acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1147b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
1148b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
1149b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
1150b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
1151b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
1152b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
1153b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
1154b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
1155b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
1156b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
1157b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1158b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
1159b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1160b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
1161b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
1162b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
1163b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
1164b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
11651769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen
1166acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1167acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.0 and 2.0.1 shipped with database version 77
1168acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1169acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
11701769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        if (fromVersion < 78) {
1171044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force a rescan of the video entries so we can update
11721769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            // latest changed DATE_TAKEN units (in milliseconds).
11731769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            db.execSQL("UPDATE video SET date_modified=0;");
11741769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        }
1175268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
1176acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1177acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.1 shipped with database version 78
1178acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1179acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1180268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        if (fromVersion < 79) {
1181268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // move /sdcard/albumthumbs to
1182268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // /sdcard/Android/data/com.android.providers.media/albumthumbs,
1183268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // and update the database accordingly
1184268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
11859be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String oldthumbspath = mExternalStoragePaths[0] + "/albumthumbs";
11869be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String newthumbspath = mExternalStoragePaths[0] + "/" + ALBUM_THUMB_FOLDER;
1187268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            File thumbsfolder = new File(oldthumbspath);
1188268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            if (thumbsfolder.exists()) {
1189268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                // move folder to its new location
1190268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                File newthumbsfolder = new File(newthumbspath);
1191268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                newthumbsfolder.getParentFile().mkdirs();
1192268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                if(thumbsfolder.renameTo(newthumbsfolder)) {
1193268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    // update the database
1194268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" +
1195268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                            oldthumbspath + "','" + newthumbspath + "');");
1196268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                }
1197268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            }
1198268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        }
1199044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen
1200044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        if (fromVersion < 80) {
1201044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force rescan of image entries to update DATE_TAKEN as UTC timestamp.
1202044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            db.execSQL("UPDATE images SET date_modified=0;");
1203044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        }
12040ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
1205166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen        if (fromVersion < 81 && !internal) {
12060ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete entries starting with /mnt/sdcard. This is for the benefit
12070ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // of users running builds between 2.0.1 and 2.1 final only, since
12080ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // users updating from 2.0 or earlier will not have such entries.
12090ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12100ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // First we need to update the _data fields in the affected tables, since
12110ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // otherwise deleting the entries will also delete the underlying files
12120ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // (via a trigger), and we want to keep them.
12130ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12140ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12150ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12160ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12170ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12180ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
1219216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12200ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Once the paths have been renamed, we can safely delete the entries
12210ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';");
12220ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM images WHERE _data IS '////';");
12230ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM video WHERE _data IS '////';");
12240ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';");
12250ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';");
12260ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_meta WHERE _data  IS '////';");
12270ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data  IS '////';");
12280ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12290ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // rename existing entries starting with /sdcard to /mnt/sdcard
12300ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta" +
12310ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12320ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists" +
12330ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12340ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images" +
12350ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12360ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video" +
12370ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12380ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails" +
12390ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12400ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails" +
12410ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12420ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art" +
12430ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12440ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12450ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete albums and artists, then clear the modification time on songs, which
12460ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // will cause the media scanner to rescan everything, rebuilding the artist and
12470ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // album tables along the way, while preserving playlists.
12480ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // We need this rescan because ICU also changed, and now generates different
12490ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // collation keys
12500ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from albums");
12510ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from artists");
12520ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
12530ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen        }
125484403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen
125584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        if (fromVersion < 82) {
1256acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // recreate this view with the correct "group by" specifier
125784403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("DROP VIEW IF EXISTS artist_info");
125884403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
125984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "SELECT artist_id AS _id, artist, artist_key, " +
1260acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album_key) AS number_of_albums, " +
126184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
126284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "GROUP BY artist_key;");
126384403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        }
1264216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1265acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /* we skipped over version 83, and reverted versions 84, 85 and 86 */
1266ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen
1267ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        if (fromVersion < 87) {
1268ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // The fastscroll thumb needs an index on the strings being displayed,
1269ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // otherwise the queries it does to determine the correct position
1270ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // becomes really inefficient
1271022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);");
1272022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);");
1273022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);");
1274ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        }
1275216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1276acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        if (fromVersion < 88) {
1277acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // Clean up a few more things from versions 84/85/86, and recreate
1278acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // the few things worth keeping from those changes.
1279acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update1;");
1280acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update2;");
1281acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update3;");
1282acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update4;");
1283acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update1;");
1284acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update2;");
1285acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update3;");
1286acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update4;");
128716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP VIEW IF EXISTS album_artists;");
1288acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);");
1289acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);");
1290acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
1291acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
1292acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
1293acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
1294acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        }
1295403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1296fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // In version 89, originally we updateBucketNames(db, "images") and
1297fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames
1298fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        //  for all files and therefore can save the update here.
1299b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1300b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (fromVersion < 91) {
1301bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // Never query by mini_thumb_magic_index
1302bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index");
1303bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1304bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // sort the items by taken date in each bucket
1305bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)");
1306bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)");
1307bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        }
1308bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1309a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
1310d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // Gingerbread ended up going to version 100, but didn't yet have the "files"
1311d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // table, so we need to create that if we're at 100 or lower. This means
1312d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // we won't be able to upgrade pre-release Honeycomb.
1313d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        if (fromVersion <= 100) {
1314afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Remove various stages of work in progress for MTP support
1315afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
1316afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS files");
131716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;");
131816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;");
131916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;");
132016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;");
1321afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;");
1322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;");
1323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;");
1324afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;");
1325afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;");
1326afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1327afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create a new table to manage all files in our storage.
1328afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // This contains a union of all the columns from the old
1329afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // images, audio_meta, videos and audio_playlist tables.
1330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TABLE files (" +
133100a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen                        "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data TEXT," +     // this can be null for playlists
1333afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_size INTEGER," +
1334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "format INTEGER," +
1335afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "parent INTEGER," +
1336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_added INTEGER," +
1337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified INTEGER," +
1338afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mime_type TEXT," +
1339afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title TEXT," +
1340afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "description TEXT," +
1341afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_display_name TEXT," +
1342afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images
1344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "picasa_id TEXT," +
1345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "orientation INTEGER," +
1346afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1347afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images and video
1348afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "latitude DOUBLE," +
1349afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "longitude DOUBLE," +
1350afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken INTEGER," +
1351afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic INTEGER," +
1352afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_id TEXT," +
1353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_display_name TEXT," +
1354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "isprivate INTEGER," +
1355afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1356afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio
1357afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title_key TEXT," +
1358afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist_id INTEGER," +
1359afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album_id INTEGER," +
1360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "composer TEXT," +
1361afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track INTEGER," +
1362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "year INTEGER CHECK(year!=0)," +
1363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_ringtone INTEGER," +
1364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_music INTEGER," +
1365afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_alarm INTEGER," +
1366afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_notification INTEGER," +
1367afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_podcast INTEGER," +
1368d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        "album_artist TEXT," +
1369afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1370afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio and video
1371afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "duration INTEGER," +
1372afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark INTEGER," +
1373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1374afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for video
1375afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist TEXT," +
1376afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album TEXT," +
1377afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "resolution TEXT," +
1378afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "tags TEXT," +
1379afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category TEXT," +
1380afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "language TEXT," +
1381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_data TEXT," +
1382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for playlists
1384afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "name TEXT," +
1385afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1386afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // media_type is used by the views to emulate the old
1387afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // images, audio_meta, videos and audio_playlist tables.
1388afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "media_type INTEGER," +
1389afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1390afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Value of _id from the old media table.
1391afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Used only for updating other tables during database upgrade.
1392afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "old_id INTEGER" +
1393afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       ");");
1394d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen
1395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX path_index ON files(_data);");
1396afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1397afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1398afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Copy all data from our obsolete tables to the new files table
139992be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
140092be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Copy audio records first, preserving the _id column.
140192be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We do this to maintain compatibility for content Uris for ringtones.
140292be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Unfortunately we cannot do this for images and videos as well.
140392be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We choose to do this for the audio table because the fragility of Uris
140492be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // for ringtones are the most common problem we need to avoid.
140592be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            db.execSQL("INSERT INTO files (_id," + AUDIO_COLUMNSv99 + ",old_id,media_type)" +
140692be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " SELECT _id," + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO +
140792be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " FROM audio_meta;");
140892be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
1409bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + IMAGE_COLUMNSv407 + ",old_id,media_type) SELECT "
1410bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + IMAGE_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;");
1411bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + VIDEO_COLUMNSv407 + ",old_id,media_type) SELECT "
1412bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + VIDEO_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;");
141316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            if (!internal) {
1414afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT "
1415afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST
1416afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + " FROM audio_playlists;");
1417afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
141816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1419afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Delete the old tables
1420afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS images");
1421afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_meta");
1422afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS video");
1423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
142416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1425afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create views to replace our old tables
1426bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNSv407 +
1427afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1429d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 +
1430d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1431d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1432bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNSv407 +
1433afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1434afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1435afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1436afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS +
1437afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1438afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_PLAYLIST + ";");
143916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            }
144036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
14419491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // create temporary index to make the updates go faster
14429491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("CREATE INDEX tmp ON files(old_id);");
14439491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1444afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update the image_id column in the thumbnails table.
1445afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files "
1446afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = thumbnails.image_id AND files.media_type = "
1447afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ");");
144836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1449afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1450d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // update audio_id in the audio_genres_map table, and
1451d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // audio_playlists_map tables and playlist_id in the audio_playlists_map table
1452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files "
1453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = "
1454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_AUDIO + ");");
1455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files "
1456afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_playlists_map.audio_id "
1457afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");");
1458d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET playlist_id = (SELECT _id FROM files "
1459d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "WHERE files.old_id = audio_playlists_map.playlist_id "
1460d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + ");");
1461afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
1462afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1463afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update video_id in the videothumbnails table.
1464afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files "
1465afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = "
1466afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ");");
1467afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
14689491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // we don't need this index anymore now
14699491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("DROP INDEX tmp;");
14709491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1471afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update indices to work on the files table
1472afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS title_idx");
1473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS album_id_idx");
1474afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS image_bucket_index");
1475afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS video_bucket_index");
1476afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS sort_index");
1477afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS titlekey_index");
1478afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS artist_id_idx");
1479afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX title_idx ON files(title);");
1480afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1481afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);");
1482afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1483afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1484afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1485afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1486afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Recreate triggers for our obsolete tables on the new files table
1487afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
1488afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
1489afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
1490afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
1491afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
149236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1493afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " +
1494afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " +
149536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1496afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
1497afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
149836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
149936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1500afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " +
150149dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " +
150236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1503afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
150436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
150536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1506afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1507afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " +
1508afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " +
1509afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1510afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
1511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
1512afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1513afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1514afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " +
1515afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " +
1516afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1517afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1518afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "SELECT _DELETE_FILE(old._data);" +
1519afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1520afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1521afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
152236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "BEGIN " +
1523afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from files where _id=old._id;" +
1524afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_playlists_map where audio_id=old._id;" +
1525afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_genres_map where audio_id=old._id;" +
152636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "END");
152736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood            }
152836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood        }
152936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1530db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (fromVersion < 301) {
1531db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("DROP INDEX IF EXISTS bucket_index");
1532db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)");
1533db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)");
1534db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
1535db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin
153620405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        if (fromVersion < 302) {
153720405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX parent_index ON files(parent);");
153820405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX format_index ON files(format);");
153920405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        }
154020405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood
15412658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        if (fromVersion < 303) {
15422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // the album disambiguator hash changed, so rescan songs and force
15432658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // albums to be updated. Artists are unaffected.
15442658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("DELETE from albums");
15452658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
15462658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
15472658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        }
15482658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
15494b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 304 && !internal) {
155051d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            // notifies host when files are deleted
155151d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
155251d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "BEGIN " +
155351d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                        "SELECT _OBJECT_REMOVED(old._id);" +
155451d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "END");
155551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
155651d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood        }
155751d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
15584b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 305 && internal) {
15594b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            // version 304 erroneously added this trigger to the internal database
15604b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
15614b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        }
15624b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood
1563fda522dc66b94057f9c6676cb8ba10bc3b13daeaMarco Nelissen        if (fromVersion < 306 && !internal) {
1564efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // The genre list was expanded and genre string parsing was tweaked, so
1565efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // rebuild the genre list
1566efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
1567efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
1568efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
1569efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
1570efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen        }
1571efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen
157265587f9c204abb2d179527dfdca009f4780e9743Ray Chen        if (fromVersion < 307 && !internal) {
157365587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // Force rescan of image entries to update DATE_TAKEN by either GPSTimeStamp or
157465587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // EXIF local time.
157565587f9c204abb2d179527dfdca009f4780e9743Ray Chen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
157665587f9c204abb2d179527dfdca009f4780e9743Ray Chen                    + FileColumns.MEDIA_TYPE_IMAGE + ";");
157765587f9c204abb2d179527dfdca009f4780e9743Ray Chen        }
157865587f9c204abb2d179527dfdca009f4780e9743Ray Chen
1579e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // Honeycomb went up to version 307, ICS started at 401
1580e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
15814f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // Database version 401 did not add storage_id to the internal database.
15824f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // We need it there too, so add it in version 402
15834f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        if (fromVersion < 401 || (fromVersion == 401 && internal)) {
15849be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Add column for MTP storage ID
15859be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("ALTER TABLE files ADD COLUMN storage_id INTEGER;");
15869be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Anything in the database before this upgrade step will be in the primary storage
15879be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("UPDATE files SET storage_id=" + MtpStorage.getStorageId(0) + ";");
15889be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
15899be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
159078b2885edc406273d688536b0eadfea006b20662Marco Nelissen        if (fromVersion < 403 && !internal) {
159178b2885edc406273d688536b0eadfea006b20662Marco Nelissen            db.execSQL("CREATE VIEW audio_genres_map_noid AS " +
159278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    "SELECT audio_id,genre_id from audio_genres_map;");
159378b2885edc406273d688536b0eadfea006b20662Marco Nelissen        }
159478b2885edc406273d688536b0eadfea006b20662Marco Nelissen
15959289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        if (fromVersion < 404) {
15969289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // There was a bug that could cause distinct same-named albums to be
15979289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // combined again. Delete albums and force a rescan.
15989289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("DELETE from albums");
15999289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16009289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
16019289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        }
16029289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen
1603957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        if (fromVersion < 405) {
1604957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            // Add is_drm column.
1605957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("ALTER TABLE files ADD COLUMN is_drm INTEGER;");
1606957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1607957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("DROP VIEW IF EXISTS audio_meta");
1608957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv405 +
1609957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1610957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1611957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1612957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            recreateAudioView(db);
1613957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        }
1614957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1615b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (fromVersion < 407) {
16167ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            // Rescan files in the media database because a new column has been added
1617b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // in table files in version 405 and to recover from problems populating
1618b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // the genre tables
16197ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            db.execSQL("UPDATE files SET date_modified=0;");
16207ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang        }
16217ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang
1622bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        if (fromVersion < 408) {
1623bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Add the width/height columns for images and video
1624bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN width INTEGER;");
1625bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN height INTEGER;");
1626bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1627bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Rescan files to fill the columns
1628bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("UPDATE files SET date_modified=0;");
1629bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1630bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Update images and video views to contain the width/height columns
1631bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS images");
1632bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS video");
1633bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS +
1634bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1635bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1636bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS +
1637bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1638bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1639bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        }
1640bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
16415809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        if (fromVersion < 409 && !internal) {
16425809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // A bug that prevented numeric genres from being parsed was fixed, so
16435809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // rebuild the genre list
16445809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16455809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
16465809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
16475809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
16485809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        }
16495809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen
1650e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // ICS went out with database version 409, JB started at 500
1651e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1652166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        if (fromVersion < 500) {
1653166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1654166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS videothumbnails_cleanup;");
1655166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        }
1656a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        if (fromVersion < 501) {
1657a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1658a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // the images_cleanup trigger would delete the image file and the entry
1659a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // in the thumbnail table, which in turn would trigger thumbnails_cleanup
1660a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // to delete the thumbnail image
1661a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup;");
1662a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup;");
1663a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        }
16645afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        if (fromVersion < 502) {
16655afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
16665afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup;");
16675afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        }
16685118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        if (fromVersion < 503) {
16695118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            // genre and playlist cleanup now done in mediaprovider code, instead of in a trigger
16705118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
16715118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
16725118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        }
167390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (fromVersion < 504) {
167490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            // add an index to help with case-insensitive matching of paths
167590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            db.execSQL(
167690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    "CREATE INDEX IF NOT EXISTS path_index_lower ON files(_data COLLATE NOCASE);");
167790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
16787074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen        if (fromVersion < 505) {
16797074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            // Starting with schema 505 we fill in the width/height/resolution columns for videos,
16807074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            // so force a rescan of videos to fill in the blanks
16817074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16827074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen                    + FileColumns.MEDIA_TYPE_VIDEO + ";");
16837074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen        }
168459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen        if (fromVersion < 506) {
168559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // sd card storage got moved to /storage/sdcard0
168659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // first delete everything that already got scanned in /storage before this
168759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // update step was added
168859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
168959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM files WHERE _data LIKE '/storage/%';");
169059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data LIKE '/storage/%';");
169159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data LIKE '/storage/%';");
169259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data LIKE '/storage/%';");
169359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // then rename everything from /mnt/sdcard/ to /storage/sdcard0,
169459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // and from /mnt/external1 to /storage/sdcard1
169559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE files SET " +
169659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
169759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE files SET " +
169859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
169959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE album_art SET " +
170059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
170159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE album_art SET " +
170259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
170359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE thumbnails SET " +
170459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
170559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE thumbnails SET " +
170659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
170759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE videothumbnails SET " +
170859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
170959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE videothumbnails SET " +
171059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
171159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen
171259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            if (!internal) {
171359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
171459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                    "BEGIN " +
171559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                        "SELECT _OBJECT_REMOVED(old._id);" +
171659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                    "END");
171759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            }
171859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen        }
1719a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin        if (fromVersion < 507) {
1720a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin            // we update _data in version 506, we need to update the bucket_id as well
1721677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            updateBucketNames(db);
1722a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin        }
172346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen        if (fromVersion < 508 && !internal) {
172446e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            // ensure we don't get duplicate entries in the genre map
172546e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map_tmp (" +
172646e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "_id INTEGER PRIMARY KEY," +
172746e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "audio_id INTEGER NOT NULL," +
172846e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "genre_id INTEGER NOT NULL," +
172946e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "UNIQUE (audio_id,genre_id) ON CONFLICT IGNORE" +
173046e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    ");");
173146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("INSERT INTO audio_genres_map_tmp (audio_id,genre_id)" +
173246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    " SELECT DISTINCT audio_id,genre_id FROM audio_genres_map;");
173346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("DROP TABLE audio_genres_map;");
173446e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("ALTER TABLE audio_genres_map_tmp RENAME TO audio_genres_map;");
173546e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen        }
1736988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1737988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        if (fromVersion < 509) {
1738988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS log (time DATETIME PRIMARY KEY, message TEXT);");
1739988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        }
1740395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1741395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        // Emulated external storage moved to user-specific paths
1742395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        if (fromVersion < 510 && Environment.isExternalStorageEmulated()) {
1743395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            // File.fixSlashes() removes any trailing slashes
1744395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            final String externalStorage = Environment.getExternalStorageDirectory().toString();
174557b65f10aaafc4bf42a0fa59eb2bbe6c8371c2e6Jeff Sharkey            Log.d(TAG, "Adjusting external storage paths to: " + externalStorage);
1746395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1747395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            final String[] tables = {
1748395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                    TABLE_FILES, TABLE_ALBUM_ART, TABLE_THUMBNAILS, TABLE_VIDEO_THUMBNAILS };
1749395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            for (String table : tables) {
1750395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                db.execSQL("UPDATE " + table + " SET " + "_data='" + externalStorage
1751395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                        + "'||SUBSTR(_data,17) WHERE _data LIKE '/storage/sdcard0/%';");
1752395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            }
1753395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        }
17548bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey        if (fromVersion < 511) {
17558bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey            // we update _data in version 510, we need to update the bucket_id as well
17568bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey            updateBucketNames(db);
17578bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey        }
1758395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1759e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // JB 4.2 went out with database version 511, starting next release with 600
1760e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1761e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        if (fromVersion < 600) {
1762e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // modify _data column to be unique and collate nocase. Because this drops the original
1763e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // table and replaces it with a new one by the same name, we need to also recreate all
1764e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // indices and triggers that refer to the files table.
1765e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // Views don't need to be recreated.
1766e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1767e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE TABLE files2 (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1768e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_data TEXT UNIQUE" +
1769e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    // the internal filesystem is case-sensitive
1770e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    (internal ? "," : " COLLATE NOCASE,") +
1771e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_size INTEGER,format INTEGER,parent INTEGER,date_added INTEGER," +
1772e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "date_modified INTEGER,mime_type TEXT,title TEXT,description TEXT," +
1773e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_display_name TEXT,picasa_id TEXT,orientation INTEGER,latitude DOUBLE," +
1774e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER,bucket_id TEXT," +
1775e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "bucket_display_name TEXT,isprivate INTEGER,title_key TEXT,artist_id INTEGER," +
1776e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "album_id INTEGER,composer TEXT,track INTEGER,year INTEGER CHECK(year!=0)," +
1777e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "is_ringtone INTEGER,is_music INTEGER,is_alarm INTEGER," +
1778e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "is_notification INTEGER,is_podcast INTEGER,album_artist TEXT," +
1779e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "duration INTEGER,bookmark INTEGER,artist TEXT,album TEXT,resolution TEXT," +
1780e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "tags TEXT,category TEXT,language TEXT,mini_thumb_data TEXT,name TEXT," +
1781e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "media_type INTEGER,old_id INTEGER,storage_id INTEGER,is_drm INTEGER," +
1782e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "width INTEGER, height INTEGER);");
1783e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1784e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // copy data from old table, squashing entries with duplicate _data
1785e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("INSERT OR REPLACE INTO files2 SELECT * FROM files;");
1786e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("DROP TABLE files;");
1787e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("ALTER TABLE files2 RENAME TO files;");
1788e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1789e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // recreate indices and triggers
1790e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1791e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1792e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX bucket_index on files(bucket_id,media_type," +
1793e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "datetaken, _id);");
1794e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX bucket_name on files(bucket_id,media_type," +
1795e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "bucket_display_name);");
1796e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX format_index ON files(format);");
1797e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1798e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX parent_index ON files(parent);");
1799e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX path_index ON files(_data);");
1800e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1801e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX title_idx ON files(title);");
1802e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1803e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            if (!internal) {
1804e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                db.execSQL("CREATE TRIGGER audio_playlists_cleanup DELETE ON files" +
1805e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " WHEN old.media_type=4" +
1806e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " BEGIN DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1807e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        "SELECT _DELETE_FILE(old._data);END;");
1808e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                db.execSQL("CREATE TRIGGER files_cleanup DELETE ON files" +
1809e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " BEGIN SELECT _OBJECT_REMOVED(old._id);END;");
1810e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            }
1811e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        }
1812e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1813f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen        if (fromVersion < 601) {
1814f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            // remove primary key constraint because column time is not necessarily unique
1815f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS log_tmp (time DATETIME, message TEXT);");
1816f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("DELETE FROM log_tmp;");
1817f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("INSERT INTO log_tmp SELECT time, message FROM log order by rowid;");
1818f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("DROP TABLE log;");
1819f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("ALTER TABLE log_tmp RENAME TO log;");
1820f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen        }
1821f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen
1822acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sanityCheck(db, fromVersion);
1823988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000;
1824988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion
1825988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                + " in " + elapsedSeconds + " seconds");
1826988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    }
1827988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1828988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    /**
1829988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen     * Write a persistent diagnostic message to the log table.
1830988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen     */
1831988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    static void logToDb(SQLiteDatabase db, String message) {
1832934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen        db.execSQL("INSERT OR REPLACE" +
1833934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen                " INTO log (time,message) VALUES (strftime('%Y-%m-%d %H:%M:%f','now'),?);",
1834988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                new String[] { message });
1835988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        // delete all but the last 500 rows
1836988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        db.execSQL("DELETE FROM log WHERE rowid IN" +
1837f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen                " (SELECT rowid FROM log ORDER BY rowid DESC LIMIT 500,-1);");
18381d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    }
18391d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen
18401d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    /**
1841216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * Perform a simple sanity check on the database. Currently this tests
1842216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * whether all the _data entries in audio_meta are unique
1843216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     */
1844216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen    private static void sanityCheck(SQLiteDatabase db, int fromVersion) {
1845216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c1 = db.query("audio_meta", new String[] {"count(*)"},
1846216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1847216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"},
1848216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1849216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.moveToFirst();
1850216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.moveToFirst();
1851216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num1 = c1.getInt(0);
1852216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num2 = c2.getInt(0);
1853216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.close();
1854216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.close();
1855216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        if (num1 != num2) {
1856216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            Log.e(TAG, "audio_meta._data column is not unique while upgrading" +
1857216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                    " from schema " +fromVersion + " : " + num1 +"/" + num2);
1858216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            // Delete all audio_meta rows so they will be rebuilt by the media scanner
1859216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("DELETE FROM audio_meta;");
1860216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        }
1861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
1864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
1865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
1866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
1867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
1868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
1869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
187095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1872677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen     * Update the bucket_id and bucket_display_name columns for images and videos
1873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1876677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen    private static void updateBucketNames(SQLiteDatabase db) {
1877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
1878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
1881677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            // update only images and videos
1882677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            Cursor cursor = db.query("files", columns, "media_type=1 OR media_type=3",
1883677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen                    null, null, null, null);
1884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
18879491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                String [] rowId = new String[1];
1888988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                ContentValues values = new ContentValues();
1889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
1891988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    rowId[0] = cursor.getString(idColumnIndex);
1892d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    if (data != null) {
1893988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        values.clear();
1894d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        computeBucketValues(data, values);
1895677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen                        db.update("files", values, "_id=?", rowId);
1896d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    } else {
1897d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        Log.w(TAG, "null data at id " + rowId);
1898d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    }
1899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
1911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
1912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
1916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
1917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
1920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
1924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
1925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
1926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
1928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
1929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
1930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
1931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
1932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
1933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
1934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1944988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
1948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
1953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
1954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
1957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
1958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
1959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
1960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
1961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
1963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
1964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
1965d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood        values.put(ImageColumns.BUCKET_ID, path.hashCode());
1966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
1967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
1972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
1975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
1976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
1977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
1978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
1979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
1981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1983b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
1984498b62c2912302a23532c73a028a7684c5df33caRay Chen     * Copy taken time from date_modified if we lost the original value (e.g. after factory reset)
1985498b62c2912302a23532c73a028a7684c5df33caRay Chen     * This works for both video and image tables.
1986b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     *
1987b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     * @param values the content values, where taken time is updated.
1988b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     */
1989b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    private static void computeTakenTime(ContentValues values) {
1990b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        if (! values.containsKey(Images.Media.DATE_TAKEN)) {
1991b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // This only happens when MediaScanner finds an image file that doesn't have any useful
1992b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // reference to get this value. (e.g. GPSTimeStamp)
1993498b62c2912302a23532c73a028a7684c5df33caRay Chen            Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED);
1994b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            if (lastModified != null) {
1995b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                values.put(Images.Media.DATE_TAKEN, lastModified * 1000);
1996b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            }
1997b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        }
1998b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    }
1999b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen
2000b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    /**
2001b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
2002b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
2003b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
2004b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
2005b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
2006b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
2007b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
2008b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
2009b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c == null) return false;
2010b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2011b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
2012b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2013b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c.moveToFirst()) {
2014e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen            long id = c.getLong(0);
2015b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String path = c.getString(1);
2016b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            long magic = c.getLong(2);
2017b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
20189299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            MediaThumbRequest req = requestMediaThumbnail(path, origUri,
20199299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    MediaThumbRequest.PRIORITY_HIGH, magic);
20209299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            if (req == null) {
20219299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                return false;
20229299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            }
20239299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            synchronized (req) {
20249299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                try {
20259299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    while (req.mState == MediaThumbRequest.State.WAIT) {
20269299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        req.wait();
202720434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
20289299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                } catch (InterruptedException e) {
20299299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    Log.w(TAG, e);
20309299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                }
20319299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                if (req.mState == MediaThumbRequest.State.DONE) {
20329299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    result = true;
2033b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2034b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
2035b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2036b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        c.close();
2037b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2038b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
2039b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
2040b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2041e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid,
2042e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo) {
2043e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllOrigId = (id == -1);
2044e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllGroupId = (gid == -1);
2045e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        return (req.mCallingPid == pid) &&
2046e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllGroupId || req.mGroupId == gid) &&
2047e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllOrigId || req.mOrigId == id) &&
2048e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (req.mIsVideo == isVideo);
2049e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    }
2050e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
2051b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
2052b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
2053b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
2054b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
2055b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
2056b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
2057b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
2058b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
2059b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
2060b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2061b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
2062b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
2063b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
2064b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
2065b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
2066b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2067b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2068b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
206920434e032e498b716f87cce2f23dd646819218bfRay Chen        boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel"));
2070e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        Uri origUri = uri.buildUpon().encodedPath(
2071e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                uri.getPath().replaceFirst("thumbnails", "media"))
2072e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                .appendPath(origId).build();
2073b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2074b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
207520434e032e498b716f87cce2f23dd646819218bfRay Chen            Log.w(TAG, "original media doesn't exist or it's canceled.");
2076b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
207720434e032e498b716f87cce2f23dd646819218bfRay Chen        } else if (cancelRequest) {
2078e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            String groupId = uri.getQueryParameter("group_id");
2079e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo = "video".equals(uri.getPathSegments().get(1));
208020434e032e498b716f87cce2f23dd646819218bfRay Chen            int pid = Binder.getCallingPid();
208120434e032e498b716f87cce2f23dd646819218bfRay Chen            long id = -1;
2082e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            long gid = -1;
2083e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
208420434e032e498b716f87cce2f23dd646819218bfRay Chen            try {
208520434e032e498b716f87cce2f23dd646819218bfRay Chen                id = Long.parseLong(origId);
2086e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                gid = Long.parseLong(groupId);
208720434e032e498b716f87cce2f23dd646819218bfRay Chen            } catch (NumberFormatException ex) {
208820434e032e498b716f87cce2f23dd646819218bfRay Chen                // invalid cancel request
208920434e032e498b716f87cce2f23dd646819218bfRay Chen                return false;
209020434e032e498b716f87cce2f23dd646819218bfRay Chen            }
2091e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
209220434e032e498b716f87cce2f23dd646819218bfRay Chen            synchronized (mMediaThumbQueue) {
2093e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                if (mCurrentThumbRequest != null &&
2094e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                        matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) {
209520434e032e498b716f87cce2f23dd646819218bfRay Chen                    synchronized (mCurrentThumbRequest) {
209620434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL;
209720434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.notifyAll();
209820434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
209920434e032e498b716f87cce2f23dd646819218bfRay Chen                }
210020434e032e498b716f87cce2f23dd646819218bfRay Chen                for (MediaThumbRequest mtq : mMediaThumbQueue) {
2101e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                    if (matchThumbRequest(mtq, pid, id, gid, isVideo)) {
210220434e032e498b716f87cce2f23dd646819218bfRay Chen                        synchronized (mtq) {
210320434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.mState = MediaThumbRequest.State.CANCEL;
210420434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.notifyAll();
210520434e032e498b716f87cce2f23dd646819218bfRay Chen                        }
210620434e032e498b716f87cce2f23dd646819218bfRay Chen
210720434e032e498b716f87cce2f23dd646819218bfRay Chen                        mMediaThumbQueue.remove(mtq);
210820434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
210920434e032e498b716f87cce2f23dd646819218bfRay Chen                }
211020434e032e498b716f87cce2f23dd646819218bfRay Chen            }
2111b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2112b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2113b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
2114b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
2115b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2116b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
2117b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
2118b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
2119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
2121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
2122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
2123baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        List<String> prependArgs = new ArrayList<String>();
2124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
212501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
2126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
2127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
2128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
2129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
2130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
21320027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
21330027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                c.addRow(new String[] {mMediaScannerVolume});
21340027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                return c;
2135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
21380027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // Used temporarily (until we have unique media IDs) to get an identifier
21390027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // for the current sd card, so that the music app doesn't have to use the
21400027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // non-public getFatVolumeId method
21410027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        if (table == FS_ID) {
21420027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"fsid"});
21430027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            c.addRow(new Integer[] {mVolumeId});
21440027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            return c;
21450027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        }
21460027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
2147704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        if (table == VERSION) {
2148704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"version"});
214990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            c.addRow(new Integer[] {getDatabaseVersion(getContext())});
2150704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            return c;
2151704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        }
2152704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen
2153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
215410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
215510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
2157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
215810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
215910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getReadableDatabase();
21605fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) return null;
2161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
21624574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
2163c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String filter = uri.getQueryParameter("filter");
2164c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String [] keywords = null;
2165c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        if (filter != null) {
2166c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            filter = Uri.decode(filter).trim();
2167c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            if (!TextUtils.isEmpty(filter)) {
2168c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                String [] searchWords = filter.split(" ");
2169c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                keywords = new String[searchWords.length];
2170c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; i < searchWords.length; i++) {
2171c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    String key = MediaStore.Audio.keyFor(searchWords[i]);
2172c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("\\", "\\\\");
2173c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("%", "\\%");
2174c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("_", "\\_");
2175c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    keywords[i] = key;
2176c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2177c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            }
2178c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        }
2179db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (uri.getQueryParameter("distinct") != null) {
2180db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            qb.setDistinct(true);
2181db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
2182c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen
2183b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
2184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
2186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2202baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2203baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2207b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2208b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
2209b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
2210b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2211b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2215d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2216ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.equalsIgnoreCase("is_music=1")
2217ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                          || selection.equalsIgnoreCase("is_podcast=1") )
2218c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2219c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2220ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting songs");
2221ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2222ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2223ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio");
2224c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2225c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2226c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2227c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2228c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2229c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2230baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'");
2231baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2232c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2233ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
2238baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2239baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
2245baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_genres_map WHERE audio_id=?)");
2246baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2251baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2252baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
2258baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_playlists_map WHERE audio_id=?)");
2259baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2264baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2265baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2274baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2275baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2278bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen            case AUDIO_GENRES_ALL_MEMBERS:
2279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
228078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                {
228178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // if simpleQuery is true, we can do a simpler query on just audio_genres_map
228278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // we can do this if we have no keywords and our projection includes just columns
228378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // from audio_genres_map
228478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    boolean simpleQuery = (keywords == null && projectionIn != null
228578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            && (selection == null || selection.equalsIgnoreCase("genre_id=?")));
228678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (projectionIn != null) {
228778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; i < projectionIn.length; i++) {
228878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            String p = projectionIn[i];
228978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (p.equals("_id")) {
229078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // note, this is different from playlist below, because
229178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // "_id" used to (wrongly) be the audio id in this query, not
229278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // the row id of the entry in the map, and we preserve this
229378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // behavior for backwards compatibility
229478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
229578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
229678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (simpleQuery && !(p.equals("audio_id") ||
229778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    p.equals("genre_id"))) {
229878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
229978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
230078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
230178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
230278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (simpleQuery) {
230378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid");
2304bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2305baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere("genre_id=?");
2306baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2307bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
230878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    } else {
230978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid, audio");
2310bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        qb.appendWhere("audio._id = audio_id");
2311bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2312baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere(" AND genre_id=?");
2313baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2314bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
231578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; keywords != null && i < keywords.length; i++) {
231678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(" AND ");
231778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
231878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.ALBUM_KEY +
231978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.TITLE_KEY +
2320baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                    " LIKE ? ESCAPE '\\'");
2321baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add("%" + keywords[i] + "%");
232278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
232378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
232478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                }
2325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2333baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2334baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2337b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2339e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // if simpleQuery is true, we can do a simpler query on just audio_playlists_map
2340e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // we can do this if we have no keywords and our projection includes just columns
2341e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // from audio_playlists_map
23424382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                boolean simpleQuery = (keywords == null && projectionIn != null
23434382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                        && (selection == null || selection.equalsIgnoreCase("playlist_id=?")));
234497e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
234597e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
2346e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        String p = projectionIn[i];
2347e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (simpleQuery && !(p.equals("audio_id") ||
2348e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                p.equals("playlist_id") || p.equals("play_order"))) {
2349e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                            simpleQuery = false;
2350e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        }
2351e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (p.equals("_id")) {
235297e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
235397e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
2354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2356e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                if (simpleQuery) {
2357e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map");
2358baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("playlist_id=?");
2359baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2360e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                } else {
2361e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map, audio");
2362baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("audio._id = audio_id AND playlist_id=?");
2363baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2364e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2365e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(" AND ");
2366e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2367e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2368e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.TITLE_KEY +
2369baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2370baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2371e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    }
2372c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2373b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                if (table == AUDIO_PLAYLISTS_ID_MEMBERS_ID) {
2374baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere(" AND audio_playlists_map._id=?");
2375baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(5));
2376b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                }
2377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2384baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2385baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2388b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
2389b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2390b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
2391b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
2392b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2393b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2394b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2395b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
2397d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2398ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2399c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2400c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2401ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting artists");
2402ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2403ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct artist_id)";
2404ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2405ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2406ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("artist_info");
2407c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2408c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2409c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2410c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2411c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2412baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2413baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2414c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2415ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
2419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
2420baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2421baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
2425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
2426acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
2427acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        " audio.album_id=album_art.album_id");
2428acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
2429baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "artists_albums_map WHERE artist_id=?)");
2430baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(aid);
2431c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; keywords != null && i < keywords.length; i++) {
2432c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(" AND ");
2433c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2434c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            "||" + MediaStore.Audio.Media.ALBUM_KEY +
2435baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            " LIKE ? ESCAPE '\\'");
2436baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add("%" + keywords[i] + "%");
2437c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2438acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                groupBy = "audio.album_id";
2439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
2440acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
2441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
2442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
2443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
2446d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2447ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2448c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2449c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2450ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting albums");
2451ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2452ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct album_id)";
2453ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2454ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2455ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("album_info");
2456c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2457c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2458c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2459c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2460c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2461c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2462baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2463baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2464c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2465ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
2469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
2470baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2471baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
2475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
2476baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("album_id=?");
2477baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2480a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
2481a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
2482a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
2483a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
2484a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
2485baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection,
2486baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        combine(prependArgs, selectionArgs), sort, table, limit);
2487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
248816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
2489e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
2490baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2491baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(2));
2492b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                // fall through
249316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
2494e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
249516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                qb.setTables("files");
2496b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                break;
2497b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2498e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            case MTP_OBJECT_REFERENCES:
2499e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int handle = Integer.parseInt(uri.getPathSegments().get(2));
250010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                return getObjectReferences(helper, db, handle);
2501e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
2503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
2504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2506baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection,
2507baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        //        combine(prependArgs, selectionArgs), groupBy, null, sort, limit));
2508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
2509baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
2510b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
2512ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            String nonotify = uri.getQueryParameter("nonotify");
2513ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            if (nonotify == null || !nonotify.equals("1")) {
2514ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen                c.setNotificationUri(getContext().getContentResolver(), uri);
2515ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            }
2516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2517b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
2519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2521baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    private String[] combine(List<String> prepend, String[] userArgs) {
2522baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int presize = prepend.size();
2523baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        if (presize == 0) {
2524baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            return userArgs;
2525baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2526baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2527baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int usersize = (userArgs != null) ? userArgs.length : 0;
2528baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        String [] combined = new String[presize + usersize];
2529baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < presize; i++) {
2530baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[i] = prepend.get(i);
2531baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2532baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < usersize; i++) {
2533baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[presize + i] = userArgs[i];
2534baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2535baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        return combined;
2536baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    }
2537baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
2539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
25404574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
25414574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
2542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
254318c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen        String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment();
2544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
2545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
2547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
2548a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
2549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
2550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
2551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
2552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
25533001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            String key = MediaStore.Audio.keyFor(searchWords[i]);
25543001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("\\", "\\\\");
25553001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("%", "\\%");
25563001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("_", "\\_");
2557a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
2558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
25593001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                        searchWords[i].equals("the")) ? "%" : "%" + key + "%";
2560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2562a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
2563a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
2564a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
25653001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where = "match LIKE ? ESCAPE '\\'";
2566a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
25673001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where += " AND match LIKE ? ESCAPE '\\'";
2568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2571a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
2572a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
2573a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
2574a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
2575a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
2576a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
2577a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
2578a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
2579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
25804574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
2581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
2585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
2587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2591c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case FILES_ID:
259226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                Cursor c = null;
259326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                try {
259426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    c = query(url, MIME_TYPE_PROJECTION, null, null, null);
259526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null && c.getCount() == 1) {
259626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.moveToFirst();
259726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        String mimeType = c.getString(1);
259826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.deactivate();
259926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        return mimeType;
260026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
260126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                } finally {
260226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null) {
260326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.close();
260426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
2605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
2610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
2611804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen            case AUDIO_ALBUMART_ID:
2612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
2614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
2617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
2619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
2623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
2626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
2632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
2635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2636804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen        throw new IllegalStateException("Unknown URL : " + url);
2637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
264150c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen     * present a new filename is generated. The file itself is not created.
2642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
2644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
2645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
2647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
2648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
2649801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        String file = initialValues.getAsString(MediaStore.MediaColumns.DATA);
2650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
2651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
2652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
2653801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            values.put(MediaStore.MediaColumns.DATA, file);
2654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
2656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
265892013781ab1573eee3d5d75682f320fe3a6076f2Marco Nelissen        // we used to create the file here, but now defer this until openFile() is called
2659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
2660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2662d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectAdded(long objectHandle) {
266334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
266434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
266534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
266634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectAdded((int)objectHandle);
266734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
266834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectAdded", e);
266934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
267034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2671d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2672d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2673d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2674d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2675d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectRemoved(long objectHandle) {
267634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
267734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
267834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
267934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectRemoved((int)objectHandle);
268034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
268134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectRemoved", e);
268234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
268334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2684d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2685d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2686d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2687d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
2690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
2692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
2693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
269410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
269510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
269910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
27005fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) {
27015fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            throw new IllegalStateException("Couldn't open database for " + uri);
27025fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        }
2703ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2704ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
2705ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            return playlistBulkInsert(db, uri, values);
2706e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } else if (match == MTP_OBJECT_REFERENCES) {
2707e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            int handle = Integer.parseInt(uri.getPathSegments().get(2));
270810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            return setObjectReferences(helper, db, handle, values);
2709ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2710ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
27112dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
27132dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
2714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
2715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
2717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
2718801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                if (values[i] != null) {
27192dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                    insertInternal(uri, match, values[i], notifyRowIds);
2720801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                }
2721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
2726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
27272dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
27282dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        // Notify MTP (outside of successful transaction)
27292dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        notifyMtp(notifyRowIds);
27302dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
2732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
2733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2736801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood    public Uri insert(Uri uri, ContentValues initialValues) {
27375d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        int match = URI_MATCHER.match(uri);
27382dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
27392dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
27402dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
27412dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        notifyMtp(notifyRowIds);
27422dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
27435d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // do not signal notification for MTP objects.
27445d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // we will signal instead after file transfer is successful.
2745e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        if (newUri != null && match != MTP_OBJECTS) {
2746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
2747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
2749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
27512dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    private void notifyMtp(ArrayList<Long> rowIds) {
27522dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        int size = rowIds.size();
27532dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        for (int i = 0; i < size; i++) {
27542dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke            sendObjectAdded(rowIds.get(i).longValue());
27552dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        }
27562dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    }
27572dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2758ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) {
2759ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        DatabaseUtils.InsertHelper helper =
2760ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            new DatabaseUtils.InsertHelper(db, "audio_playlists_map");
27618b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
27628b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID);
27638b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
27648b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        long playlistId = Long.parseLong(uri.getPathSegments().get(3));
2765ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2766ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        db.beginTransaction();
2767ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        int numInserted = 0;
2768ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        try {
2769ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            int len = values.length;
2770ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            for (int i = 0; i < len; i++) {
27718b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.prepareForInsert();
27728b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // getting the raw Object and converting it long ourselves saves
27738b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // an allocation (the alternative is ContentValues.getAsLong, which
27748b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // returns a Long object)
27758b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                long audioid = ((Number) values[i].get(
27768b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue();
27778b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(audioidcolidx, audioid);
27788b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playlistididx, playlistId);
27798b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // convert to int ourselves to save an allocation.
27808b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                int playorder = ((Number) values[i].get(
27818b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue();
27828b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playorderidx, playorder);
27838b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.execute();
2784ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            }
2785ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            numInserted = len;
2786ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.setTransactionSuccessful();
2787ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        } finally {
2788ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.endTransaction();
2789ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            helper.close();
2790ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2791ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        getContext().getContentResolver().notifyChange(uri, null);
2792ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        return numInserted;
2793ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    }
2794ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
279510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertDirectory(DatabaseHelper helper, SQLiteDatabase db, String path) {
279610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (LOCAL_LOGV) Log.v(TAG, "inserting directory " + path);
2797ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        ContentValues values = new ContentValues();
2798ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION);
2799ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.DATA, path);
280010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        values.put(FileColumns.PARENT, getParent(helper, db, path));
28019be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        values.put(FileColumns.STORAGE_ID, getStorageId(path));
2802f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        File file = new File(path);
2803f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        if (file.exists()) {
2804f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
2805f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        }
280610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumInserts++;
2807ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2808ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        sendObjectAdded(rowId);
2809ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        return rowId;
2810ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
2811ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
281210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getParent(DatabaseHelper helper, SQLiteDatabase db, String path) {
2813b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        int lastSlash = path.lastIndexOf('/');
2814b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (lastSlash > 0) {
2815b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String parentPath = path.substring(0, lastSlash);
28169be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            for (int i = 0; i < mExternalStoragePaths.length; i++) {
28179be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (parentPath.equals(mExternalStoragePaths[i])) {
28189be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return 0;
28199be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
2820b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
28217f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Long cid = mDirectoryCache.get(parentPath);
28227f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            if (cid != null) {
28237f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath);
28247f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return cid;
28257f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            }
28267f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
2827e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            String selection = MediaStore.MediaColumns.DATA + "=?";
2828b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String [] selargs = { parentPath };
282910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
28307f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
2831b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            try {
28327f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                long id;
2833b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c == null || c.getCount() == 0) {
2834b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    // parent isn't in the database - so add it
283510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    id = insertDirectory(helper, db, parentPath);
28367f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath);
2837b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                } else {
28385461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    if (c.getCount() > 1) {
28395461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        Log.e(TAG, "more than one match for " + parentPath);
28405461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    }
2841b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    c.moveToFirst();
28427f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    id = c.getLong(0);
28437f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath);
2844b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                }
28457f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.put(parentPath, id);
28467f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return id;
2847b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            } finally {
2848b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c != null) c.close();
2849b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
2850b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        } else {
2851b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            return 0;
2852b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        }
2853b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    }
2854b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
28559be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private int getStorageId(String path) {
28569be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        for (int i = 0; i < mExternalStoragePaths.length; i++) {
28579be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String test = mExternalStoragePaths[i];
28589be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (path.startsWith(test)) {
28599be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int length = test.length();
28609be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (path.length() == length || path.charAt(length) == '/') {
28619be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return MtpStorage.getStorageId(i);
28629be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
28639be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
28649be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
28659be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        // default to primary storage
28669be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        return MtpStorage.getStorageId(0);
28679be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    }
28689be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
286910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, int mediaType,
28702dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                            boolean notify, ArrayList<Long> notifyRowIds) {
287110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
2872afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        ContentValues values = null;
2873afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2874afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        switch (mediaType) {
2875afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_IMAGE: {
287610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".jpg", "DCIM/Camera");
2877afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2878afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2879afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String data = values.getAsString(MediaColumns.DATA);
2880afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
2881afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    computeDisplayName(data, values);
2882afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2883afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2884afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2885afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2886afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2887afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_AUDIO: {
2888afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // SQLite Views are read-only, so we need to deconstruct this
2889afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // insert and do inserts into the underlying tables.
2890afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // If doing this here turns out to be a performance bottleneck,
2891afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // consider moving this to native code and using triggers on
2892afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the view.
2893afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values = new ContentValues(initialValues);
2894afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
28952658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
28962658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
28972658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                values.remove(MediaStore.Audio.Media.COMPILATION);
28982658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
2899afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Insert the artist into the artist table and remove it from
2900afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the input values
2901afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                Object so = values.get("artist");
2902afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String s = (so == null ? "" : so.toString());
2903afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("artist");
2904afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long artistRowId;
290510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> artistCache = helper.mArtistCache;
2906801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String path = values.getAsString(MediaStore.MediaColumns.DATA);
2907afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(artistCache) {
2908afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = artistCache.get(s);
2909afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
291010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        artistRowId = getKeyIdForName(helper, db,
291110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "artists", "artist_key", "artist",
2912afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, s, path, 0, null, artistCache, uri);
2913afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
2914afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        artistRowId = temp.longValue();
2915afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
2916afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2917afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String artist = s;
2918afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2919afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Do the same for the album field
2920afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.get("album");
2921afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
2922afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("album");
2923afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long albumRowId;
292410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> albumCache = helper.mAlbumCache;
2925afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(albumCache) {
29262658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    int albumhash = 0;
29272658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    if (albumartist != null) {
29282658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = albumartist.hashCode();
29292658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else if (compilation != null && compilation.equals("1")) {
29302658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        // nothing to do, hash already set
29312658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else {
29322658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
29332658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    }
2934afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    String cacheName = s + albumhash;
2935afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = albumCache.get(cacheName);
2936afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
293710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        albumRowId = getKeyIdForName(helper, db,
293810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "albums", "album_key", "album",
2939afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, cacheName, path, albumhash, artist, albumCache, uri);
2940afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
2941afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        albumRowId = temp;
2942afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
2943afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
29445d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
2945afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("artist_id", Integer.toString((int)artistRowId));
2946afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("album_id", Integer.toString((int)albumRowId));
2947afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.getAsString("title");
2948afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
2949afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title_key", MediaStore.Audio.keyFor(s));
2950afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // do a final trim of the title, in case it started with the special
2951afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // "sort first" character (ascii \001)
2952afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("title");
2953afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title", s.trim());
2954b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2955801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                computeDisplayName(values.getAsString(MediaStore.MediaColumns.DATA), values);
2956afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2957afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2958afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2959afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_VIDEO: {
296010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".3gp", "video");
2961801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String data = values.getAsString(MediaStore.MediaColumns.DATA);
2962afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeDisplayName(data, values);
2963afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2964afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2965afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2966afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2967afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2968afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (values == null) {
2969afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(initialValues);
2970afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2971fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // compute bucket_id and bucket_display_name for all files
2972afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String path = values.getAsString(MediaStore.MediaColumns.DATA);
2973fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        if (path != null) {
2974fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            computeBucketValues(path, values);
2975fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        }
2976fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2977afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2978afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        long rowId = 0;
2979afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer i = values.getAsInteger(
2980afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
2981afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (i != null) {
2982afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = i.intValue();
2983afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(values);
2984afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
2985afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2986afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2987afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String title = values.getAsString(MediaStore.MediaColumns.TITLE);
29883572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen        if (title == null && path != null) {
2989c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            title = MediaFile.getFileTitle(path);
2990c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2991c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        values.put(FileColumns.TITLE, title);
2992c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2993afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE);
2994afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer formatObject = values.getAsInteger(FileColumns.FORMAT);
2995c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        int format = (formatObject == null ? 0 : formatObject.intValue());
2996c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format == 0) {
299763b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang            if (TextUtils.isEmpty(path)) {
2998c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                // special case device created playlists
2999afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
3000c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST);
3001c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    // create a file path for the benefit of MTP
30029be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    path = mExternalStoragePaths[0]
3003afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            + "/Playlists/" + values.getAsString(Audio.Playlists.NAME);
3004c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(MediaStore.MediaColumns.DATA, path);
300510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    values.put(FileColumns.PARENT, getParent(helper, db, path));
3006c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                } else {
300763b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang                    Log.e(TAG, "path is empty in insertFile()");
3008c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                }
3009c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            } else {
3010c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                format = MediaFile.getFormatCode(path, mimeType);
3011c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3012c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3013c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format != 0) {
3014c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.FORMAT, format);
3015c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            if (mimeType == null) {
3016c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                mimeType = MediaFile.getMimeTypeForFormatCode(format);
3017c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3018c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3019c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
3020801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        if (mimeType == null && path != null) {
3021c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            mimeType = MediaFile.getMimeTypeForFile(path);
3022c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3023c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (mimeType != null) {
3024c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.MIME_TYPE, mimeType);
3025afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3026f1f6a9e343033de33fc748f659b9221f8d5b06e1Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_NONE && !MediaScanner.isNoMediaPath(path)) {
3027afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int fileType = MediaFile.getFileTypeForMimeType(mimeType);
3028afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (MediaFile.isAudioFileType(fileType)) {
3029afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_AUDIO;
3030afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isVideoFileType(fileType)) {
3031afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_VIDEO;
3032afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isImageFileType(fileType)) {
3033afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_IMAGE;
3034afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isPlayListFileType(fileType)) {
3035afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
3036afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3037afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
3038c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3039afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        values.put(FileColumns.MEDIA_TYPE, mediaType);
3040c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
3041afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (rowId == 0) {
3042afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
30433572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen                String name = values.getAsString(Audio.Playlists.NAME);
3044282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                if (name == null && path == null) {
3045282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                    // MediaScanner will compute the name from the path if we have one
3046afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
3047282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                            "no name was provided when inserting abstract playlist");
3048afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3049afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            } else {
3050a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu                if (path == null) {
3051afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // path might be null for playlists created on the device
3052afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // or transfered via MTP
3053afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
3054afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "no path was provided when inserting new file");
3055afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3056e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3057b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3058f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            // make sure modification date and size are set
3059f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            if (path != null) {
3060f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                File file = new File(path);
3061f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                if (file.exists()) {
3062f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                    values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
306334d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                    if (!values.containsKey(FileColumns.SIZE)) {
306434d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                        values.put(FileColumns.SIZE, file.length());
306534d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                    }
306620368a558593c685623bfff6df1f4545d53f8291Jia Su                    // make sure date taken time is set
306720368a558593c685623bfff6df1f4545d53f8291Jia Su                    if (mediaType == FileColumns.MEDIA_TYPE_IMAGE
306820368a558593c685623bfff6df1f4545d53f8291Jia Su                            || mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
306920368a558593c685623bfff6df1f4545d53f8291Jia Su                        computeTakenTime(values);
307020368a558593c685623bfff6df1f4545d53f8291Jia Su                    }
3071e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
30725d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
3073b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3074afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            Long parent = values.getAsLong(FileColumns.PARENT);
30755d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (parent == null) {
3076e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path != null) {
307710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    long parentId = getParent(helper, db, path);
307816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.PARENT, parentId);
3079e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
30809be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
30819be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            Integer storage = values.getAsInteger(FileColumns.STORAGE_ID);
30829be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (storage == null) {
30839be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int storageId = getStorageId(path);
30849be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                values.put(FileColumns.STORAGE_ID, storageId);
30855d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
3086b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
308710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumInserts++;
3088afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
3089afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId);
3090afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
309110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (rowId != -1 && notify) {
30922dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                notifyRowIds.add(rowId);
3093afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
30945d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } else {
309510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates++;
309616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.update("files", values, FileColumns._ID + "=?",
3097afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    new String[] { Long.toString(rowId) });
30985d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        }
3099d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen        if (format == MtpConstants.FORMAT_ASSOCIATION) {
3100d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen            mDirectoryCache.put(path, rowId);
3101d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen        }
3102afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3103afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        return rowId;
31041717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    }
31051717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
310610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private Cursor getObjectReferences(DatabaseHelper helper, SQLiteDatabase db, int handle) {
310710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
3108a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
3109e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
3110e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
3111e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
3112e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
3113afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long playlistId = c.getLong(0);
3114afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
3115afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
3116e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
3117e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return null;
3118e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
311910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumQueries++;
3120e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                return db.rawQuery(OBJECT_REFERENCES_QUERY,
3121afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        new String[] { Long.toString(playlistId) } );
3122e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3123e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
3124e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
3125e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
3126e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3127e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3128e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return null;
3129e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
3130e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
313110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int setObjectReferences(DatabaseHelper helper, SQLiteDatabase db,
313210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            int handle, ContentValues values[]) {
3133e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // first look up the media table and media ID for the object
3134e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        long playlistId = 0;
313510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
3136a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
3137e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
3138e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
3139e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
3140e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
3141afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
3142afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
3143e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
3144e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return 0;
3145e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3146afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                playlistId = c.getLong(0);
3147e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3148e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
3149e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
3150e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
3151e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3152e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3153e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (playlistId == 0) {
3154e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            return 0;
3155e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3156e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3157e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // next delete any existing entries
315810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumDeletes++;
315910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        db.delete("audio_playlists_map", "playlist_id=?",
3160e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] { Long.toString(playlistId) });
3161e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3162e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // finally add the new entries
3163e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int count = values.length;
3164e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int added = 0;
3165e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        ContentValues[] valuesList = new ContentValues[count];
3166e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        for (int i = 0; i < count; i++) {
3167e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // convert object ID to audio ID
3168e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long audioId = 0;
3169e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID);
317010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
3171a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            c = db.query("files", sMediaTableColumns, "_id=?",
3172e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    new String[] {  Long.toString(objectId) },
3173e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    null, null, null);
3174e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            try {
3175e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null && c.moveToNext()) {
3176afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    int mediaType = c.getInt(1);
317750d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood                    if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) {
3178e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        // we only allow audio files in playlists, so skip
3179e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        continue;
3180e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    }
3181afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    audioId = c.getLong(0);
3182e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3183e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            } finally {
3184e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null) {
3185e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    c.close();
3186e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3187e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3188e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (audioId != 0) {
3189e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                ContentValues v = new ContentValues();
3190e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId);
3191e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
3192a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added);
3193a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                valuesList[added++] = v;
3194e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3195e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3196e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (added < count) {
3197e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // we weren't able to find everything on the list, so lets resize the array
3198e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // and pass what we have.
3199e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            ContentValues[] newValues = new ContentValues[added];
3200e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            System.arraycopy(valuesList, 0, newValues, 0, added);
3201e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            valuesList = newValues;
3202e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3203e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return playlistBulkInsert(db,
3204e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId),
3205e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                valuesList);
3206e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
3207e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3208b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private static final String[] GENRE_LOOKUP_PROJECTION = new String[] {
3209b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres._ID, // 0
3210b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres.NAME, // 1
3211b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    };
3212b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3213b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private void updateGenre(long rowId, String genre) {
3214b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri uri = null;
3215b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Cursor cursor = null;
3216b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri genresUri = MediaStore.Audio.Genres.getContentUri("external");
3217b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        try {
3218b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // see if the genre already exists
3219b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            cursor = query(genresUri, GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?",
3220b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            new String[] { genre }, null);
3221b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor == null || cursor.getCount() == 0) {
3222b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre does not exist, so create the genre in the genre table
3223b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                ContentValues values = new ContentValues();
3224b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                values.put(MediaStore.Audio.Genres.NAME, genre);
3225b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = insert(genresUri, values);
3226b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            } else {
3227b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre already exists, so compute its Uri
3228b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.moveToNext();
3229b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = ContentUris.withAppendedId(genresUri, cursor.getLong(0));
3230b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3231b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (uri != null) {
3232b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = Uri.withAppendedPath(uri, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY);
3233b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3234b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        } finally {
3235b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // release the cursor if it exists
3236b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor != null) {
3237b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.close();
3238b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3239b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3240b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3241b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (uri != null) {
3242b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // add entry to audio_genre_map
3243b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            ContentValues values = new ContentValues();
3244b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
3245b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            insert(uri, values);
3246b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3247b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    }
3248b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
32492dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
32502dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                               ArrayList<Long> notifyRowIds) {
3251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
3252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3253d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
3254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3256bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
325710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
325810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
325910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
326010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
326110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
326210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStartTime = SystemClock.currentTimeMicro();
326310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
3265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3267b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
326838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String path = null;
3269b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3270b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3271b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
327238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            path = initialValues.getAsString(MediaStore.MediaColumns.DATA);
3273b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3274b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
327538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
327710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
327810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null && match != VOLUMES && match != MTP_CONNECTED) {
3279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
328210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
3283819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null
328410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                : helper.getWritableDatabase());
3285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
3287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
32882dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
32892dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds);
3290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
3292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
3293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3297b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
3298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
329910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3300b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
330110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
3303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
3305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContentUri(uri.getPathSegments().get(0)), rowId);
3306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3310b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
3311b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
331210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3313b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
331410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3315b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
3316b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
3317b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
3318b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            getContentUri(uri.getPathSegments().get(0)), rowId);
3319b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
3320b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3321b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
3322b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
33242dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
33252dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_AUDIO, true, notifyRowIds);
3326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
3328b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    if (genre != null) {
3329b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        updateGenre(rowId, genre);
3330b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    }
3331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
3336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3337bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
333910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3340ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_genres_map", "genre_id", values);
3341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
3348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3349bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
335110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
3353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
3354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
336110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3362bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                rowId = db.insert("audio_genres", "audio_id", initialValues);
3363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
3365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
3370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
3371bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
337310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
3375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
3382bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
33842dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, values,
33852dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_PLAYLIST, true, notifyRowIds);
3386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
3388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
3394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
3395bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
339710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3398ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_playlists_map", "playlist_id", values);
3399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
34062dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
34072dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds);
3408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3409b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
3410b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            uri.getPathSegments().get(0)), rowId);
3411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3415c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case AUDIO_ALBUMART: {
341610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                if (helper.mInternal) {
3417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
3418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3419bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = null;
3420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
3421bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
3422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
3423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
3424bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = initialValues;
3425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
342610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3427801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
3428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3432c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
34355619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            {
34365619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                String name = initialValues.getAsString("name");
34375619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                Uri attachedVolume = attachVolume(name);
34385619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) {
34395619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    DatabaseHelper dbhelper = getDatabaseForUri(attachedVolume);
34405619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    if (dbhelper == null) {
34415619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                        Log.e(TAG, "no database for attached volume " + attachedVolume);
34425619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    } else {
34435619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                        dbhelper.mScanStartTime = SystemClock.currentTimeMicro();
34445619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    }
34455619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                }
34465619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                return attachedVolume;
34475619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            }
3448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3449819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            case MTP_CONNECTED:
345034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                synchronized (mMtpServiceConnection) {
345134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    if (mMtpService == null) {
345234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        Context context = getContext();
345334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        // MTP is connected, so grab a connection to MtpService
345434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        context.bindService(new Intent(context, MtpService.class),
345534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                                mMtpServiceConnection, Context.BIND_AUTO_CREATE);
345634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    }
345734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
3458819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                break;
3459819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
3460afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FILES:
346110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
34622dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_NONE, true, notifyRowIds);
3463fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                if (rowId > 0) {
3464fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                    newUri = Files.getContentUri(uri.getPathSegments().get(0), rowId);
3465fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                }
3466fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                break;
3467fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood
3468e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
34692dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                // We don't send a notification if the insert originated from MTP
347010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
34712dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_NONE, false, notifyRowIds);
3472afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (rowId > 0) {
3473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    newUri = Files.getMtpObjectsUri(uri.getPathSegments().get(0), rowId);
34745d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                }
34755d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                break;
34765d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
3477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
3479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
348138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path != null && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
348238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            // need to set the media_type of all the files below this folder to 0
348338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processNewNoMediaPath(helper, db, path);
348438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
3485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
3486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
348838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
348938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Sets the media type of all files below the newly added .nomedia file or
349038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * hidden folder to 0, so the entries no longer appear in e.g. the audio and
349138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * images views.
349238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     *
349338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * @param path The path to the new .nomedia file or hidden directory
349438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
34955461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    private void processNewNoMediaPath(final DatabaseHelper helper, final SQLiteDatabase db,
34965461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            final String path) {
34975461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        final File nomedia = new File(path);
349838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (nomedia.exists()) {
34995461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            hidePath(helper, db, path);
35005461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        } else {
35015461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            // File doesn't exist. Try again in a little while.
35025461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            // XXX there's probably a better way of doing this
35035461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            new Thread(new Runnable() {
35045461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                @Override
35055461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                public void run() {
35065461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    SystemClock.sleep(2000);
35075461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    if (nomedia.exists()) {
35085461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        hidePath(helper, db, path);
35095461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    } else {
35105461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        Log.w(TAG, "does not exist: " + path, new Exception());
35115461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    }
35125461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                }}).start();
351338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
351438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
351538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
35165461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    private void hidePath(DatabaseHelper helper, SQLiteDatabase db, String path) {
35175461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        File nomedia = new File(path);
35185461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        String hiddenroot = nomedia.isDirectory() ? path : nomedia.getParent();
35195461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        ContentValues mediatype = new ContentValues();
35205461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        mediatype.put("media_type", 0);
35215461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        int numrows = db.update("files", mediatype,
3522a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                "_data >= ? AND _data < ?",
3523a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang                new String[] { hiddenroot  + "/", hiddenroot + "0"});
35245461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        helper.mNumUpdates += numrows;
35255461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        ContentResolver res = getContext().getContentResolver();
35265461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        res.notifyChange(Uri.parse("content://media/"), null);
35275461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    }
35285461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen
352938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
353038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Rescan files for missing metadata and set their type accordingly.
353138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * There is code for detecting the removal of a nomedia file or renaming of
353238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * a directory from hidden to non-hidden in the MediaScanner and MtpDatabase,
353338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * both of which call here.
353438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
353538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private void processRemovedNoMediaPath(final String path) {
353638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        final DatabaseHelper helper;
353738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path.startsWith(mExternalStoragePaths[0])) {
353838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
353938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        } else {
354038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
354138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
354238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
354338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        new ScannerClient(getContext(), db, path);
354438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
354538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
354638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private static final class ScannerClient implements MediaScannerConnectionClient {
354738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String mPath = null;
354838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        MediaScannerConnection mScannerConnection;
354938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase mDb;
355038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
355138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public ScannerClient(Context context, SQLiteDatabase db, String path) {
355238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mDb = db;
355338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mPath = path;
355438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection = new MediaScannerConnection(context, this);
355538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.connect();
355638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
355738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
355838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
355938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onMediaScannerConnected() {
35605461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            Cursor c = mDb.query("files", openFileColumns,
3561a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                    "_data >= ? AND _data < ?",
3562a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang                    new String[] { mPath + "/", mPath + "0"},
35635461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    null, null, null);
356438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            while (c.moveToNext()) {
356538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                String d = c.getString(0);
356638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(d);
356738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (f.isFile()) {
356838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    mScannerConnection.scanFile(d, null);
356938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                }
357038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            }
357138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.disconnect();
357238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            c.close();
357338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
357438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
357538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
357638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onScanCompleted(String path, Uri uri) {
357738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
357838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
357938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3580cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    @Override
3581cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
3582cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                throws OperationApplicationException {
3583cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3584cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // The operations array provides no overall information about the URI(s) being operated
3585cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // on, so begin a transaction for ALL of the databases.
3586cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
3587cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
3588cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase idb = ihelper.getWritableDatabase();
3589cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        idb.beginTransaction();
3590cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase edb = null;
3591cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (ehelper != null) {
3592cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb = ehelper.getWritableDatabase();
3593cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb.beginTransaction();
3594cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3595cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        try {
3596cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentProviderResult[] result = super.applyBatch(operations);
3597cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.setTransactionSuccessful();
3598cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3599cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.setTransactionSuccessful();
3600cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3601cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // Rather than sending targeted change notifications for every Uri
3602cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // affected by the batch operation, just invalidate the entire internal
3603cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // and external name space.
3604cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentResolver res = getContext().getContentResolver();
3605cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
3606cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            return result;
3607cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        } finally {
3608cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.endTransaction();
3609cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3610cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.endTransaction();
3611cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3612cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3613cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    }
3614cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3615cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
36169299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) {
3617b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
3618e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            MediaThumbRequest req = null;
3619e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            try {
3620e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                req = new MediaThumbRequest(
36219299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        getContext().getContentResolver(), path, uri, priority, magic);
3622e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                mMediaThumbQueue.add(req);
3623e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                // Trigger the handler.
3624e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
3625e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                msg.sendToTarget();
3626e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            } catch (Throwable t) {
3627e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Log.w(TAG, t);
3628e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            }
3629b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
3630b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
3631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
3632b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
3634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
3635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
3636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
3637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
3639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
3640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
3641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
3642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
36439be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            return mExternalStoragePaths[0] + "/" + directoryName + "/" + name + preferredExtension;
3644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private boolean ensureFileExists(String path) {
3648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
3649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
3650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
3651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
3653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
3654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
3655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
3656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
3657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
3658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
3659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
366017ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood            file.getParentFile().mkdirs();
3661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
366217ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood                return file.createNewFile();
3663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
3664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
3665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
3667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
3671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
3672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
3673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
3676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
3677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
3679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
3680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
3681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
36829f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
3683afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3684afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE;
36859f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
36869f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
3687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
3688afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
3690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3692b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
3693b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3694b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
3695b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
3696b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3697b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
3699afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3700afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO;
3701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
3704afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
3709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
3714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
3717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
3718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
3720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
3725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
3728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
3731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
3735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
3740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
3742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
3745afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3746afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST;
3747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3750afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
3755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
3757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
3760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
3762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
3763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
3766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
3767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
3768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
3771afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3772afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO;
3773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
3776afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3780b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
3781b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3782b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
3783b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
3784b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3785b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
378616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
3787e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
37881717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                where = "_id=" + uri.getPathSegments().get(2);
378916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
3790e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
379116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                out.table = "files";
37921717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                break;
37931717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
3794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
3797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
3800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
3801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
3802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
3803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
3804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
3805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
3808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
3812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
3813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
3815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
3819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
3820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
382110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
382210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
382310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
382410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
382510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
382610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStopTime = SystemClock.currentTimeMicro();
3827988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                String msg = dump(database, false);
3828988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                logToDb(database.getWritableDatabase(), msg);
382910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
3831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
3832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3834819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        if (match == VOLUMES_ID) {
3835819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            detachVolume(uri);
3836819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            count = 1;
3837819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else if (match == MTP_CONNECTED) {
383834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (mMtpServiceConnection) {
383934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                if (mMtpService != null) {
384034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // MTP has disconnected, so release our connection to MtpService
384134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    getContext().unbindService(mMtpServiceConnection);
384234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 1;
384334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // mMtpServiceConnection.onServiceDisconnected might not get called,
384434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // so set mMtpService = null here
384534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
384634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } else {
384734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 0;
384834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
384934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
3850819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else {
3851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
3852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
3853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3854819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                        "Unknown URI: " + uri + " match: " + match);
3855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
385610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            database.mNumDeletes++;
3857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
3858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
3860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
3861a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
3862a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                if (sGetTableAndWhereParam.table.equals("files")) {
3863d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
3864d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    if (deleteparam == null || ! deleteparam.equals("false")) {
3865d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        database.mNumQueries++;
3866d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
3867d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sMediaTypeDataId,
3868d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
3869d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        String [] idvalue = new String[] { "" };
38704eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        String [] playlistvalues = new String[] { "", "" };
3871d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        while (c.moveToNext()) {
3872d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            int mediatype = c.getInt(0);
3873d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            if (mediatype == FileColumns.MEDIA_TYPE_IMAGE) {
3874d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                try {
3875d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Libcore.os.remove(c.getString(1));
3876d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    idvalue[0] =  "" + c.getLong(2);
3877d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    database.mNumQueries++;
3878d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Cursor cc = db.query("thumbnails", sDataOnlyColumn,
3879d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                            "image_id=?", idvalue, null, null, null);
3880d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    while (cc.moveToNext()) {
3881d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                        Libcore.os.remove(cc.getString(0));
3882d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    }
3883d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    cc.close();
3884d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    database.mNumDeletes++;
3885d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    db.delete("thumbnails", "image_id=?", idvalue);
3886d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                } catch (ErrnoException e) {
3887a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                                }
3888d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_VIDEO) {
3889d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                try {
3890d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Libcore.os.remove(c.getString(1));
3891d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                } catch (ErrnoException e) {
3892d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                }
3893d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_AUDIO) {
3894f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                if (!database.mInternal) {
3895f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                    idvalue[0] =  "" + c.getLong(2);
38969b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                    database.mNumDeletes += 2; // also count the one below
3897f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                    db.delete("audio_genres_map", "audio_id=?", idvalue);
38984eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    // for each playlist that the item appears in, move
38994eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    // all the items behind it forward by one
39004eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    Cursor cc = db.query("audio_playlists_map",
39014eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                            sPlaylistIdPlayOrder,
39024eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                            "audio_id=?", idvalue, null, null, null);
39034eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    while (cc.moveToNext()) {
39044eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        playlistvalues[0] = "" + cc.getLong(0);
39054eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        playlistvalues[1] = "" + cc.getInt(1);
39069b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                        database.mNumUpdates++;
39074eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        db.execSQL("UPDATE audio_playlists_map" +
39084eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                " SET play_order=play_order-1" +
39094eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                " WHERE playlist_id=? AND play_order>?",
39104eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                playlistvalues);
39114eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    }
39124eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    cc.close();
3913f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                    db.delete("audio_playlists_map", "audio_id=?", idvalue);
3914f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                }
3915d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_PLAYLIST) {
3916d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                // TODO, maybe: remove the audio_playlists_cleanup trigger and implement
3917d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                // it functionality here (clean up the playlist map)
39185afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen                            }
3919a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                        }
3920d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        c.close();
3921a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    }
3922a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                }
3923a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
3924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
392536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    case MTP_OBJECTS:
3926e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood                    case MTP_OBJECTS_ID:
3927d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        try {
3928d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            // don't send objectRemoved event since this originated from MTP
3929d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = true;
393010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            database.mNumDeletes++;
393110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            count = db.delete("files", sGetTableAndWhereParam.where, whereArgs);
3932d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        } finally {
3933d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = false;
3934d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        }
393536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        break;
393678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    case AUDIO_GENRES_ID_MEMBERS:
393710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
393878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        count = db.delete("audio_genres_map",
393978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
394078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        break;
3941166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
3942a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS_ID:
3943a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS:
3944166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS_ID:
3945166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS:
3946166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        // Delete the referenced files first.
3947166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
3948166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sDataOnlyColumn,
3949166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
3950166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        if (c != null) {
3951166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            while (c.moveToNext()) {
3952166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                try {
3953166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                    Libcore.os.remove(c.getString(0));
3954166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                } catch (ErrnoException e) {
3955166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                }
3956166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            }
3957166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            c.close();
3958166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        }
3959166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        database.mNumDeletes++;
3960166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        count = db.delete(sGetTableAndWhereParam.table,
3961166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
3962166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        break;
3963166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
3964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
396510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
3966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
3967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
3968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
3969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
39703631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // Since there are multiple Uris that can refer to the same files
39713631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // and deletes can affect other objects in storage (like subdirectories
39723631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // or playlists) we will notify a change on the entire volume to make
39733631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // sure no listeners miss the notification.
39743631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                String volume = uri.getPathSegments().get(0);
39753631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volume);
39763631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                getContext().getContentResolver().notifyChange(notifyUri, null);
3977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
3981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
398438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    public Bundle call(String method, String arg, Bundle extras) {
398538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (MediaStore.UNHIDE_CALL.equals(method)) {
398638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processRemovedNoMediaPath(arg);
398738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            return null;
398838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
398938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        throw new UnsupportedOperationException("Unsupported call: " + method);
399038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
399138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
399238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    @Override
3993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
3994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
3995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3996b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
3997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
399810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
399910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
4000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
4001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
4002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
400310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumUpdates++;
400410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
400510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
4006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4007b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
4008b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
4009b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
4010b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
4011b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
4012b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
4013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
4014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
4015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
40161d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // special case renaming directories via MTP.
40171d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // in this case we must update all paths in the database with
40181d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // the directory name as a prefix
40191d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID)
40201d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    && initialValues != null && initialValues.size() == 1) {
40211d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                String oldPath = null;
4022801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA);
40237f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.remove(newPath);
40241d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                // MtpDatabase will rename the directory first, so we test the new file name
402538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(newPath);
402638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (newPath != null && f.isDirectory()) {
402710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumQueries++;
40281d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION,
40291d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        userWhere, whereArgs, null, null, null);
40301d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    try {
40311d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null && cursor.moveToNext()) {
40321d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            oldPath = cursor.getString(1);
40331d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
40341d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    } finally {
40351d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null) cursor.close();
40361d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
40371d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    if (oldPath != null) {
40387f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                        mDirectoryCache.remove(oldPath);
40391d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        // first rename the row for the directory
404010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
40411d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        count = db.update(sGetTableAndWhereParam.table, initialValues,
40421d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
40431d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0) {
40449bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                            // update the paths of any files and folders contained in the directory
40455461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                            Object[] bindArgs = new Object[] {newPath, oldPath.length() + 1,
4046a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang                                    oldPath + "/", oldPath + "0",
40479bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    // update bucket_display_name and bucket_id based on new path
40489bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    f.getName(),
40499bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    f.toString().toLowerCase().hashCode()
40509bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    };
405110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
40525461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                            db.execSQL("UPDATE files SET _data=?1||SUBSTR(_data, ?2)" +
40539bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    // also update bucket_display_name
40549bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    ",bucket_display_name=?6" +
40559bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    ",bucket_id=?7" +
4056a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                                    " WHERE _data >= ?3 AND _data < ?4;",
40575461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    bindArgs);
40581d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
40591d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
40601d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0 && !db.inTransaction()) {
40611d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            getContext().getContentResolver().notifyChange(uri, null);
40621d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
406338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        if (f.getName().startsWith(".")) {
406438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            // the new directory name is hidden
406538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            processNewNoMediaPath(helper, db, newPath);
406638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        }
40671d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        return count;
40681d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
406938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                } else if (newPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
407038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    processNewNoMediaPath(helper, db, newPath);
40711d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                }
40721d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            }
40731d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
4074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
4075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
4076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
4077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
4078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
40792658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
40802658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
40812658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        values.remove(MediaStore.Audio.Media.COMPILATION);
408207656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen
4083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
4084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
4085a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
40866006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("artist");
4087a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
4088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
408910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> artistCache = helper.mArtistCache;
4090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
4091a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
4092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
409310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    artistRowId = getKeyIdForName(helper, db,
409410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "artists", "artist_key", "artist",
4095acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
4096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
4097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
4098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
4099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
4101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
410359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
4104a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
41056006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("album");
4106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
4107801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                            String path = values.getAsString(MediaStore.MediaColumns.DATA);
410859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
41092658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            if (albumartist != null) {
41102658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                albumHash = albumartist.hashCode();
41112658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            } else if (compilation != null && compilation.equals("1")) {
41122658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                // nothing to do, hash already set
411359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
41149289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path == null) {
41159289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    if (match == AUDIO_MEDIA) {
41169289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Log.w(TAG, "Possible multi row album name update without"
41179289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                + " path could give wrong album key");
41189289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    } else {
41199289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        //Log.w(TAG, "Specify path to avoid extra query");
41209289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Cursor c = query(uri,
41219289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                new String[] { MediaStore.Audio.Media.DATA},
41229289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                null, null, null);
41239289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        if (c != null) {
41249289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            try {
41259289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                int numrows = c.getCount();
41269289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                if (numrows == 1) {
41279289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    c.moveToFirst();
41289289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    path = c.getString(0);
41299289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                } else {
41309289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    Log.e(TAG, "" + numrows + " rows for " + uri);
41319289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                }
41329289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            } finally {
41339289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                c.close();
41349289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            }
41359289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        }
41369289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    }
41379289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
41389289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path != null) {
41399289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
41409289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
414159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
41422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
4143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
4144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
414510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> albumCache = helper.mAlbumCache;
4146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
414759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
414859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
4149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
415010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    albumRowId = getKeyIdForName(helper, db,
415110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "albums", "album_key", "album",
4152a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
4153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
4154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
4155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
4156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
4158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
4161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
4162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
4163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
4164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
4165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
4166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
4167e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
4168e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
4169e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
4170e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
4171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
417310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
4174afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        count = db.update(sGetTableAndWhereParam.table, values,
4175afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
4176b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        if (genre != null) {
4177b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            if (count == 1 && match == AUDIO_MEDIA_ID) {
4178b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                long rowId = Long.parseLong(uri.getPathSegments().get(3));
4179b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                updateGenre(rowId, genre);
4180b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            } else {
4181b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                // can't handle genres for bulk update or for non-audio files
4182b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                Log.w(TAG, "ignoring genre in update: count = "
4183b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                        + count + " match = " + match);
4184b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            }
4185b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        }
4186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
4189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
4190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
4191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
4192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
4193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
4194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
4195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
4196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
4197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
4198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
4199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
4200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
4201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
4202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
4203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4204b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                        computeTakenTime(values);
420510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
4206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
4207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
420801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
420901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
421001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
421101a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
421210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumQueries++;
421301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
421401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
421501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
4216b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
4217216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                try {
4218216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    while (c.moveToNext()) {
4219216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        long magic = c.getLong(2);
4220216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        if (magic == 0) {
4221216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                            requestMediaThumbnail(c.getString(1), uri,
4222216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                                    MediaThumbRequest.PRIORITY_NORMAL, 0);
4223216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        }
4224b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
4225216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                } finally {
4226216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    c.close();
4227b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
4228b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
4229b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
4230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4232f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4233f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
4234f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    String moveit = uri.getQueryParameter("move");
4235f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    if (moveit != null) {
4236f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
4237f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        if (initialValues.containsKey(key)) {
4238f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int newpos = initialValues.getAsInteger(key);
4239f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            List <String> segments = uri.getPathSegments();
4240f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            long playlist = Long.valueOf(segments.get(3));
4241f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int oldpos = Integer.valueOf(segments.get(5));
424210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            return movePlaylistEntry(helper, db, playlist, oldpos, newpos);
4243f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        }
4244f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        throw new IllegalArgumentException("Need to specify " + key +
4245f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                                " when using 'move' parameter");
4246f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    }
4247f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    // fall through
4248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
424910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumUpdates++;
4250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
4251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
4252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4255cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // in a transaction, the code that began the transaction should be taking
4256cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // care of notifications once it ends the transaction successfully
4257cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (count > 0 && !db.inTransaction()) {
4258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
4259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
4261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
426310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int movePlaylistEntry(DatabaseHelper helper, SQLiteDatabase db,
426410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            long playlist, int from, int to) {
4265f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        if (from == to) {
4266f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return 0;
4267f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
4268f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        db.beginTransaction();
42695e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        int numlines = 0;
4270f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        try {
427110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates += 3;
42724eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            Cursor c = db.query("audio_playlists_map",
42734eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    new String [] {"play_order" },
42744eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order",
42754eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    from + ",1");
42764eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.moveToFirst();
42774eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            int from_play_order = c.getInt(0);
42784eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.close();
42794eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c = db.query("audio_playlists_map",
42804eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    new String [] {"play_order" },
42814eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order",
42824eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    to + ",1");
42834eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.moveToFirst();
42844eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            int to_play_order = c.getInt(0);
42854eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.close();
4286f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=-1" +
42874eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    " WHERE play_order=" + from_play_order +
4288f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " AND playlist_id=" + playlist);
4289f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // We could just run both of the next two statements, but only one of
4290f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // of them will actually do anything, so might as well skip the compile
4291f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // and execute steps.
4292f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            if (from  < to) {
4293f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" +
42944eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " WHERE play_order<=" + to_play_order +
42954eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " AND play_order>" + from_play_order +
4296f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4297f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = to - from + 1;
4298f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            } else {
4299f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" +
43004eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " WHERE play_order>=" + to_play_order +
43014eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " AND play_order<" + from_play_order +
4302f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4303f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = from - to + 1;
4304f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            }
43054eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=" + to_play_order +
4306f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=-1 AND playlist_id=" + playlist);
4307f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.setTransactionSuccessful();
4308f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        } finally {
4309f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.endTransaction();
4310f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
43115e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé
43125e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
43135e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé                .buildUpon().appendEncodedPath(String.valueOf(playlist)).build();
43145e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        // notifyChange() must be called after the database transaction is ended
43155e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        // or the listeners will read the old data in the callback
43165e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        getContext().getContentResolver().notifyChange(uri, null);
43175e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé
43185e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        return numlines;
4319f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    }
4320f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
4322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
4323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
4324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
4326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
4327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
432871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
433071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
433171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
433271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
433371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
433471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
433571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
433671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
433771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
43385fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            if (db == null) {
43395fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                throw new IllegalStateException("Couldn't open database for " + uri);
43405fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            }
434171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
434271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
434371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
434471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
434571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
434671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
434771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
434871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
434971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
435071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
435171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                String audiopath = c.getString(0);
435271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                int albumid = c.getInt(1);
435371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // Try to get existing album art for this album first, which
435471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // could possibly have been obtained from a different file.
435571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // If that fails, try to get it from this specific file.
435671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
435771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
4358007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    pfd = openFileAndEnforcePathPermissionsHelper(newUri, mode);
435971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
436071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    // That didn't work, now try to get it from the specific file
436134fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen                    pfd = getThumb(database, db, audiopath, albumid, null);
436271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
436371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
436471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            c.close();
436571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
436671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
436771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4369007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            pfd = openFileAndEnforcePathPermissionsHelper(uri, mode);
4370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
437171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
437271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
437371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
437471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
437571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
4377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
4378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
4379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
4380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
4381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
43835fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                if (db == null) {
43845fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                    throw new IllegalStateException("Couldn't open database for " + uri);
43855fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                }
4386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
438871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
4389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
4390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
4391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
4392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
4393a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen                        null, null, null, null, MediaStore.Audio.Media.TRACK);
439471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                if (c.moveToFirst()) {
4395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String audiopath = c.getString(0);
439634fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen                    pfd = getThumb(database, db, audiopath, albumid, uri);
4397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                c.close();
4399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
440071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
440171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
440271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
4403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
4405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4407007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /**
4408007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * Return the {@link MediaColumns#DATA} field for the given {@code Uri}.
4409007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     */
4410007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private File queryForDataFile(Uri uri) throws FileNotFoundException {
4411007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final Cursor cursor = query(
4412007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                uri, new String[] { MediaColumns.DATA }, null, null, null);
44138ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey        if (cursor == null) {
44148ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey            throw new FileNotFoundException("Missing cursor for " + uri);
44158ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey        }
44168ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey
4417007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
4418007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            switch (cursor.getCount()) {
4419007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                case 0:
4420007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    throw new FileNotFoundException("No entry for " + uri);
4421007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                case 1:
4422007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    if (cursor.moveToFirst()) {
4423007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        return new File(cursor.getString(0));
4424007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    } else {
4425007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        throw new FileNotFoundException("Unable to read entry for " + uri);
4426007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    }
4427007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                default:
4428007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    throw new FileNotFoundException("Multiple items at " + uri);
4429007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            }
4430007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } finally {
4431007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            cursor.close();
4432007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4433007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
4434007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4435007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /**
4436007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * Replacement for {@link #openFileHelper(Uri, String)} which enforces any
4437007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * permissions applicable to the path before returning.
4438007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     */
4439007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, String mode)
4440007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throws FileNotFoundException {
4441007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final int modeBits = ContentResolver.modeToMode(uri, mode);
4442007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0;
4443007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4444f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey        File file = queryForDataFile(uri);
4445007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final String path;
4446007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
4447007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            path = file.getCanonicalPath();
4448007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } catch (IOException e) {
4449007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throw new IllegalArgumentException("Unable to resolve canonical path for " + file, e);
4450007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4451007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4452007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        if (path.startsWith(sExternalPath)) {
44533e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen            Context c = getContext();
44543e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen            if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
44553e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                    != PackageManager.PERMISSION_GRANTED) {
44563e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                c.enforceCallingOrSelfPermission(
44573e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                        READ_EXTERNAL_STORAGE, "External path: " + path);
44583e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen            }
4459007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4460007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            if (isWrite) {
44613e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
44623e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                        != PackageManager.PERMISSION_GRANTED) {
44633e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                    c.enforceCallingOrSelfPermission(
44643e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                            WRITE_EXTERNAL_STORAGE, "External path: " + path);
44653e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                }
4466007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            }
4467f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey
44689f635dd94465e4dc324c6c4c15d081d1b18b5693Jeff Sharkey            // Bypass emulation layer when file is opened for reading, but only
4469dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey            // when opening read-only and we have an exact match.
44709f635dd94465e4dc324c6c4c15d081d1b18b5693Jeff Sharkey            if (modeBits == MODE_READ_ONLY) {
44719f635dd94465e4dc324c6c4c15d081d1b18b5693Jeff Sharkey                file = Environment.maybeTranslateEmulatedPathToInternal(file);
4472f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey            }
4473007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } else if (path.startsWith(sCachePath)) {
4474007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            getContext().enforceCallingOrSelfPermission(
4475007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    ACCESS_CACHE_FILESYSTEM, "Cache path: " + path);
447670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        } else if (isWrite) {
447770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            // don't write to non-cache, non-sdcard files.
447870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            throw new FileNotFoundException("Can't access " + file);
447970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        } else {
448070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            checkWorldReadAccess(path);
4481007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4482007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4483007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        return ParcelFileDescriptor.open(file, modeBits);
4484007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
4485007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
448670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    /**
448770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen     * Check whether the path is a world-readable file
448870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen     */
448970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    private void checkWorldReadAccess(String path) throws FileNotFoundException {
449070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
449170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        try {
449270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            StructStat stat = Libcore.os.stat(path);
449370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            int accessBits = OsConstants.S_IROTH;
449470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            if (OsConstants.S_ISREG(stat.st_mode) &&
449570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                ((stat.st_mode & accessBits) == accessBits)) {
449670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                checkLeadingPathComponentsWorldExecutable(path);
449770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                return;
449870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
449970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        } catch (ErrnoException e) {
450070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            // couldn't stat the file, either it doesn't exist or isn't
450170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            // accessible to us
450270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        }
450370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
450470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        throw new FileNotFoundException("Can't access " + path);
450570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    }
450670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
450770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    private void checkLeadingPathComponentsWorldExecutable(String filePath)
450870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            throws FileNotFoundException {
450970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        File parent = new File(filePath).getParentFile();
451070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
451170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        int accessBits = OsConstants.S_IXOTH;
451270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
451370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        while (parent != null) {
451470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            if (! parent.exists()) {
451570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                // parent dir doesn't exist, give up
451670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                throw new FileNotFoundException("access denied");
451770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
451870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            try {
451970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                StructStat stat = Libcore.os.stat(parent.getPath());
452070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                if ((stat.st_mode & accessBits) != accessBits) {
452170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                    // the parent dir doesn't have the appropriate access
452270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                    throw new FileNotFoundException("Can't access " + filePath);
452370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                }
452470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            } catch (ErrnoException e1) {
452570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                // couldn't stat() parent
452670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                throw new FileNotFoundException("Can't access " + filePath);
452770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
452870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            parent = parent.getParentFile();
452970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        }
453070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    }
453170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
4532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
453310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper;
4534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
4535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
4536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
4537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
4538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
454010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void makeThumbAsync(DatabaseHelper helper, SQLiteDatabase db,
454110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String path, long album_id) {
45428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
45438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
45448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
45458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
45468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
45478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
45488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
45498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
45508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
45518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
4552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
455310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        d.helper = helper;
4554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
4555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
4556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
4557a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
45588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
45598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
45608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
45618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
45628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
45638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
45648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
45658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
45668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
45678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
45688a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
4569b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
4570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
4571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4573f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    //Return true if the artPath is the dir as it in mExternalStoragePaths
4574f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    //for multi storage support
4575f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    private static boolean isRootStorageDir(String artPath) {
4576f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        for ( int i = 0; i < mExternalStoragePaths.length; i++) {
4577f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen            if ((mExternalStoragePaths[i] != null) &&
4578f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                    (artPath.equalsIgnoreCase(mExternalStoragePaths[i])))
4579f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                return true;
4580f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        }
4581f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        return false;
4582f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    }
4583f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen
45848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
45858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
45868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
45878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
4588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
4591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
4592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
4593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
45948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
45958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
4596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
4597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4598d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
45993f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // same directory as the media file, except if that directory is
46003f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // is the root directory of the sd card or the download directory.
4601d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
4602d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
4603d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
4604d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
4605d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
4606d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
46078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
4608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
4609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
4610d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
46113f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String artPath = path.substring(0, lastSlash);
46120fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen                    String dwndir = Environment.getExternalStoragePublicDirectory(
46132f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
4614d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4615d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
4616d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
4617d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
4618d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
4619f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                        } else if (!isRootStorageDir(artPath) &&
4620ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                                !artPath.equalsIgnoreCase(dwndir)) {
4621d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
4622d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
4623d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
4624d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
4625d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4626d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
4627d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
4628d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
4629d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
4630d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
4631d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4632d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
4633d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
4634d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
4635d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
4636d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4637d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
4638d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
4639d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
4640d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
4641d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4642d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
4643d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
4644d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4645d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
4646d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
4647d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4648d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
4649d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4650d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4651d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
4652d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
4653d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
4654d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
4655d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4656d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
46573f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        File file = new File(artPath, bestmatch);
4658d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
4659d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
4660d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
46612c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                compressed = new byte[(int)file.length()];
4662d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
4663d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
4664d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
4665d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
46662c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                            } catch (OutOfMemoryError ex) {
46672c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                Log.w(TAG, ex);
46682c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                compressed = null;
4669d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
4670d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
4671d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
4672d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
46788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
46798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
4680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
46818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
46828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
4683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
46848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
468510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    Uri getAlbumArtOutputUri(DatabaseHelper helper, SQLiteDatabase db, long album_id, Uri albumart_uri) {
46868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
46878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
46888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
46898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
4690801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            Cursor c = query(albumart_uri, new String [] { MediaStore.MediaColumns.DATA },
46918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
4692d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            try {
4693d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null && c.moveToFirst()) {
4694d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    String albumart_path = c.getString(0);
4695d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    if (ensureFileExists(albumart_path)) {
4696d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                        out = albumart_uri;
4697d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    }
4698d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                } else {
4699d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    albumart_uri = null;
4700d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                }
4701d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            } finally {
4702d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null) {
4703d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    c.close();
4704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
470671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
470771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
47088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
47098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
47108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
47118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
471210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
4713801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
47148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
47158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
471650c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen                    // ensure the parent directory exists
471750c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen                    String albumart_path = values.getAsString(MediaStore.MediaColumns.DATA);
471850c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen                    ensureFileExists(albumart_path);
4719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
47208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
47218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
47228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
47238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
47248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
47258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
47268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
47288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
47298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
473020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) throws IOException {
473120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen        OutputStream outstream = null;
47328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
473320e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            outstream = getContext().getContentResolver().openOutputStream(out);
47348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
47368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
47378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
47388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
47398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
474020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                if (!bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream)) {
474120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    throw new IOException("failed to compress bitmap");
474220e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                }
4743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
474420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen        } finally {
474520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            IoUtils.closeQuietly(outstream);
4746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
47478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
47488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
474934fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen    private ParcelFileDescriptor getThumb(DatabaseHelper helper, SQLiteDatabase db, String path,
475034fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen            long album_id, Uri albumart_uri) {
475171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
475234fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen        d.helper = helper;
475371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
475471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
475571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
475671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
475771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
475871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
475971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
476071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
47618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
4762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
47638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
476471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
47658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
47668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
47688a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
47698a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47708a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
47718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
47728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
47738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
47748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
47758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
47768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
477870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final Resources r = getContext().getResources();
477970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size);
478070676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) {
47818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
47828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
47838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
47848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
47858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
47878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
47888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
47898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
47908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
47918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
47928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
47938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
47948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
47958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
4797a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false);
4798a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    if (nbm != null && nbm != bm) {
4799a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm.recycle();
4800a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm = nbm;
4801a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    }
48028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
48038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
48048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
48058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
48068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
48078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
480871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
48098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
48108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
481171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
481271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
481371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
481471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
481524001394f571b1f0378840cbf299288e4df10508Bjorn Bringert                return ParcelFileDescriptor.fromData(compressed, "albumthumb");
481671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
481771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
481871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
4819a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This one needs to actually be saved on the sd card.
4820a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This is wrapped in a transaction because there are various things
4821a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // that could go wrong while generating the thumbnail, and we only want
4822a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // to update the database when all steps succeeded.
4823a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            d.db.beginTransaction();
482420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            Uri out = null;
482520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            ParcelFileDescriptor pfd = null;
4826a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            try {
482720e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri);
4828a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen
4829a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (out != null) {
4830a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    writeAlbumArt(need_to_recompress, out, compressed, bm);
4831a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    getContext().getContentResolver().notifyChange(MEDIA_URI, null);
483220e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    pfd = openFileHelper(out, "r");
4833a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    d.db.setTransactionSuccessful();
4834a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    return pfd;
4835a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                }
483620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            } catch (IOException ex) {
4837a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
4838a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (UnsupportedOperationException ex) {
4839a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
4840a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } finally {
4841a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                d.db.endTransaction();
4842a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (bm != null) {
4843a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    bm.recycle();
484471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
484520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                if (pfd == null && out != null) {
484620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // Thumbnail was not written successfully, delete the entry that refers to it.
484720e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // Note that this only does something if getAlbumArtOutputUri() reused an
484820e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // existing entry from the database. If a new entry was created, it will
484920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // have been rolled back as part of backing out the transaction.
485020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    getContext().getContentResolver().delete(out, null, null);
485120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                }
485271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
48538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
485471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
4855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
4859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
4860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
4861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
4862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
4863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
4864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
486559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
486659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
486759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
4868a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
4869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
4870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
4871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
4872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
4873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
487410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getKeyIdForName(DatabaseHelper helper, SQLiteDatabase db,
487510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String table, String keyField, String nameField,
487659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
4877a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
4878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
4879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
488151cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            rawName = MediaStore.UNKNOWN_STRING;
4882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
4884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
488651cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            // shouldn't happen, since we only get null keys for null inputs
488751cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            Log.e(TAG, "null key", new Exception());
4888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
4889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
489159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
4892e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen        boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName);
489359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
48942658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // To distinguish same-named albums, we append a hash. The hash is based
48952658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // on the "album artist" tag if present, otherwise on the "compilation" tag
48962658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // if present, otherwise on the path.
489759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
489859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
489959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
490059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
490159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
4902a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
490359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
4904a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
4905a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
4906a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
490759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
490859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
4909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
491010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
4911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
4912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
4915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
4916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
4917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
4918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
4919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
492010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumInserts++;
4921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
492259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
4923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
492410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            makeThumbAsync(helper, db, path, rowId);
4925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
4927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
4928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
4929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
4930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
4934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
4935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
4936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
4937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
4939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
4940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
4941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
4942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
4943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
4944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
4945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
494610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
4947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
4948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
4949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
4950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
4951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
4955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
4956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
4957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
4958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
4961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (c != null) c.close();
4962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
496459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
496559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
4966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
4968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
4972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
4973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
4974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
4975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
4976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
4978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
4979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
4980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
4981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
4983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
4984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
4985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
4987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
4988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
4990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
4991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
4992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
4993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
4994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
4995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
4996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
4997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
4998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
5002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
5003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
5004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
5005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
5006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
5007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
5010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
5011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
5016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
5018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
5019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
5021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
50225619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            if (uri.getPathSegments().size() >= 1) {
5023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
5024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
5027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5029fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isMediaDatabaseName(String name) {
5030fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
5031fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5032fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5033fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (EXTERNAL_DATABASE_NAME.equals(name)) {
5034fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5035fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5036168d49e39a356cdbd0fa04c356e3480e413d6f34kwangjung.kim        if (name.startsWith("external-") && name.endsWith(".db")) {
5037fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5038fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5039fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
5040fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
5041fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
5042fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isInternalMediaDatabaseName(String name) {
5043fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
5044fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5045fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5046fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
5047fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
5048fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
5049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
5051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
5052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
5053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
5055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
5056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
505875392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
5059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
5060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
5061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
5064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
5065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
5066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5068993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            Context context = getContext();
506910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper helper;
5070702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
507110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true,
5072fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                        false, mObjectRemovedCallback);
5073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
5074993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                if (Environment.isExternalStorageRemovable()) {
50759be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    String path = mExternalStoragePaths[0];
5076993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    int volumeID = FileUtils.getFatVolumeId(path);
5077993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
5078993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
5079ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // Must check for failure!
5080ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // If the volume is not (yet) mounted, this will create a new
5081ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // external-ffffffff.db database instead of the one we expect.  Then, if
5082ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // android.process.media is later killed and respawned, the real external
5083ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // database will be attached, containing stale records, or worse, be empty.
5084ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    if (volumeID == -1) {
5085ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        String state = Environment.getExternalStorageState();
5086ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        if (Environment.MEDIA_MOUNTED.equals(state) ||
5087ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
5088ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // This may happen if external storage was _just_ mounted.  It may also
5089ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // happen if the volume ID is _actually_ 0xffffffff, in which case it
5090ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // must be changed since FileUtils::getFatVolumeId doesn't allow for
5091ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // that.  It may also indicate that FileUtils::getFatVolumeId is broken
5092ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // (missing ioctl), which is also impossible to disambiguate.
5093ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            Log.e(TAG, "Can't obtain external volume ID even though it's mounted.");
5094ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        } else {
5095ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            Log.i(TAG, "External volume is not (yet) mounted, cannot attach.");
5096ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        }
5097ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick
5098ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        throw new IllegalArgumentException("Can't obtain external volume ID for " +
5099ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                                volume + " volume.");
5100ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    }
5101ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick
5102993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // generate database name based on volume ID
5103993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
510410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbName, false,
5105fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
5106993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    mVolumeId = volumeID;
5107993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                } else {
5108993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // external database name should be EXTERNAL_DATABASE_NAME
5109993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // however earlier releases used the external-XXXXXXXX.db naming
5110993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // for devices without removable storage, and in that case we need to convert
5111993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // to this new convention
5112993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME);
5113993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (!dbFile.exists()) {
5114993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // find the most recent external database and rename it to
5115993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // EXTERNAL_DATABASE_NAME, and delete any other older
5116993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // external database files
5117993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        File recentDbFile = null;
5118993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        for (String database : context.databaseList()) {
5119ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                            if (database.startsWith("external-") && database.endsWith(".db")) {
5120993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                File file = context.getDatabasePath(database);
5121993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                if (recentDbFile == null) {
5122993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
5123993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else if (file.lastModified() > recentDbFile.lastModified()) {
5124ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                                    context.deleteDatabase(recentDbFile.getName());
5125993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
5126993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else {
5127ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                                    context.deleteDatabase(file.getName());
5128993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                }
5129993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
5130993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
5131993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        if (recentDbFile != null) {
5132993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (recentDbFile.renameTo(dbFile)) {
5133993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.d(TAG, "renamed database " + recentDbFile.getName() +
5134993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
5135993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            } else {
5136993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.e(TAG, "Failed to rename database " + recentDbFile.getName() +
5137993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
5138993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // This shouldn't happen, but if it does, continue using
5139993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // the file under its old name
5140993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                dbFile = recentDbFile;
5141993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
5142993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
5143993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME
5144993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    }
514510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbFile.getName(), false,
5146fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
5147993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                }
5148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
5149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
5150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
515210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            mDatabases.put(volume, helper);
5153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
515410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (!helper.mInternal) {
5155ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                // create default directories (only happens on first boot)
515610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                createDefaultFolders(helper, helper.getWritableDatabase());
5157ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
5158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
51599be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                File[] files = new File(mExternalStoragePaths[0], ALBUM_THUMB_FOLDER).listFiles();
5160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
5161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
5162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
5163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
5166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
5167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
5168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
5169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
5170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
5171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
5172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (cursor != null) cursor.close();
5173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
5176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
5177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
5178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
5179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
5180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
5185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
5186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
5190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
5191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
5192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
5194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
519675392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
5197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
5198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
5199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
5202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
5203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
5204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
5205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
5206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
5207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
5208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
5211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
5212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
5213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
5215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
5216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
5217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
5218e9ee0248d62f3badef8a554f35f78e9116ef8a5cMike Lockwood            } catch (Exception e) {
5219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
5220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
5223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
5224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
5227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
5228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
5231ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer    private static final boolean LOCAL_LOGV = false;
5232971a2ef5165e2072c76bf25049fdda94187019c2Dianne Hackborn
5233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
5234993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private static final String EXTERNAL_DATABASE_NAME = "external.db";
5235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
5237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
5238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
5240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
5241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
5242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
5244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
5246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
5248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
5249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
52500027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    // current FAT volume ID
5251993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private int mVolumeId = -1;
52520027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
5253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
5254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
5255268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen    static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs";
5256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
5258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
5259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
52601717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS
526116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    // are stored in the "files" table, so do not renumber them unless you also add
52621717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // a corresponding database upgrade step for it.
5263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
5264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
5265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
5266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
5267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
5269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
5270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
5271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
5272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
5273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
5274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
5275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
5276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
5277bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen    private static final int AUDIO_GENRES_ALL_MEMBERS = 109;
5278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
5279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
5280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
5281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
5282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
5283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
5284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
5285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
5286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
5287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
5288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
528971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
5290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
5292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
5293b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
5294b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
5295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
5297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
5298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5299a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
5300a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
5301a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
5302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
5304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
53050027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private static final int FS_ID = 600;
5306704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen    private static final int VERSION = 601;
53070027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
530816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES = 700;
530916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES_ID = 701;
5310a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
5311e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    // Used only by the MTP implementation
5312e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS = 702;
5313e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS_ID = 703;
5314e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECT_REFERENCES = 704;
5315819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // UsbReceiver calls insert() and delete() with this URI to tell us
5316819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // when MTP is connected and disconnected
5317819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    private static final int MTP_CONNECTED = 705;
5318b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
5319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
5320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
5321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5322b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
5323b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
5324b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
5325b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
53261d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    private static final String[] PATH_PROJECTION = new String[] {
53271d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood        MediaStore.MediaColumns._ID,
53281d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            MediaStore.MediaColumns.DATA,
53291d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    };
53301d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
5331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
5332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
5333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
5334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
5335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5336b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
5337b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
5338b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
5339b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
5340b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
5341b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
5342e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private static final String OBJECT_REFERENCES_QUERY =
5343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map"
5344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?"
5345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER;
5346e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
5347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
5348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
5349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
5350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
5351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
5352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
5353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
5355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
5356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
5357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
5358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
5359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
5360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
5361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
5362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
5363bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen        URI_MATCHER.addURI("media", "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS);
5364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
5365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
5366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
5367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
5368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
5369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
5370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
5371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
5372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
5373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
5374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
537571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
5376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
5378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
5379b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
5380b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
5381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
5383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
53840027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        URI_MATCHER.addURI("media", "*/fs_id", FS_ID);
5385704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        URI_MATCHER.addURI("media", "*/version", VERSION);
53860027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
5387819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED);
5388819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
5389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
5390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
5391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5392b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        // Used by MTP implementation
539316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file", FILES);
539416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file/#", FILES_ID);
5395e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS);
5396e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID);
5397e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES);
5398b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
5399a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
5400a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
5401a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
5402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
5403a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
5404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
5405a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
5406a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
5407a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
5408a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
5409a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
5410a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
5411a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
5412a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
5413a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
5414a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
5415a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
5416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
541710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
541810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    @Override
541910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
542010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        Collection<DatabaseHelper> foo = mDatabases.values();
542110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        for (DatabaseHelper dbh: foo) {
5422988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            writer.println(dump(dbh, true));
5423988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        }
5424988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        writer.flush();
5425988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    }
5426988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
5427988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    private String dump(DatabaseHelper dbh, boolean dumpDbLog) {
5428988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        StringBuilder s = new StringBuilder();
5429988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        s.append(dbh.mName);
5430988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        s.append(": ");
5431988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        SQLiteDatabase db = dbh.getReadableDatabase();
5432988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        if (db == null) {
5433988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append("null");
5434988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        } else {
5435988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append("version " + db.getVersion() + ", ");
5436988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            Cursor c = db.query("files", new String[] {"count(*)"}, null, null, null, null, null);
5437988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            try {
5438988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (c != null && c.moveToFirst()) {
5439988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    int num = c.getInt(0);
5440988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    s.append(num + " rows, ");
5441988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                } else {
5442988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    s.append("couldn't get row count, ");
5443988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5444988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            } finally {
5445988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (c != null) {
5446988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    c.close();
5447988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5448988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            }
5449988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumInserts + " inserts, ");
5450988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumUpdates + " updates, ");
5451988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumDeletes + " deletes, ");
5452988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumQueries + " queries, ");
5453988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            if (dbh.mScanStartTime != 0) {
5454988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                s.append("scan started " + DateUtils.formatDateTime(getContext(),
5455988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        dbh.mScanStartTime / 1000,
5456988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        DateUtils.FORMAT_SHOW_DATE
5457988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        | DateUtils.FORMAT_SHOW_TIME
5458988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        | DateUtils.FORMAT_ABBREV_ALL));
5459988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                long now = dbh.mScanStopTime;
5460988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (now < dbh.mScanStartTime) {
5461988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    now = SystemClock.currentTimeMicro();
5462988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5463988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                s.append(" (" + DateUtils.formatElapsedTime(
5464988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        (now - dbh.mScanStartTime) / 1000000) + ")");
5465988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (dbh.mScanStopTime < dbh.mScanStartTime) {
5466988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    if (mMediaScannerVolume != null &&
5467988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            dbh.mName.startsWith(mMediaScannerVolume)) {
5468988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        s.append(" (ongoing)");
546910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    } else {
5470988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        s.append(" (scanning " + mMediaScannerVolume + ")");
5471988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    }
5472988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5473988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            }
5474988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            if (dumpDbLog) {
5475988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                c = db.query("log", new String[] {"time", "message"},
5476f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen                        null, null, null, null, "rowid");
5477988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                try {
5478988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    if (c != null) {
5479988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        while (c.moveToNext()) {
5480988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            String when = c.getString(0);
5481988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            String msg = c.getString(1);
5482988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            s.append("\n" + when + " : " + msg);
5483988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        }
548410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
548510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                } finally {
548610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (c != null) {
548710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        c.close();
548810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
548910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                }
549010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
549110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        }
5492988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        return s.toString();
549310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    }
5494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
5495