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;
22b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestigimport static android.Manifest.permission.WRITE_MEDIA_STORAGE;
23f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
24007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
25007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
26702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.app.SearchManager;
27bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.BroadcastReceiver;
28bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ComponentName;
29bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProvider;
30bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderOperation;
31bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderResult;
32bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentResolver;
33bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentUris;
34bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentValues;
35bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Context;
36bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Intent;
37bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.IntentFilter;
38bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.OperationApplicationException;
39bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ServiceConnection;
40ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.content.SharedPreferences;
41bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.UriMatcher;
423e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissenimport android.content.pm.PackageManager;
4390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissenimport android.content.pm.PackageManager.NameNotFoundException;
4470676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.content.res.Resources;
45702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
46ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils;
470027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor;
48702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
51702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap;
52702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory;
53b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.media.MediaFile;
54702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner;
5538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection;
5638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection.MediaScannerConnectionClient;
57b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
5890345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpConstants;
599be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwoodimport android.mtp.MtpStorage;
60702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
61702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
6238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.os.Bundle;
63702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
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;
84f3b67d56f2225dd458f896b15a7b36badfe8be00Elliott Hughesimport android.system.ErrnoException;
85f3b67d56f2225dd458f896b15a7b36badfe8be00Elliott Hughesimport android.system.Os;
86f3b67d56f2225dd458f896b15a7b36badfe8be00Elliott Hughesimport android.system.OsConstants;
87f3b67d56f2225dd458f896b15a7b36badfe8be00Elliott Hughesimport android.system.StructStat;
88702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
8910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.text.format.DateUtils;
90702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
91702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
92ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.IoUtils;
93ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkey
94702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
9510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.FileDescriptor;
96702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
97702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
98702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
99702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
10010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.PrintWriter;
101cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList;
10210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.util.Collection;
103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
106f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List;
10738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport java.util.Locale;
108b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
1098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
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;
1305943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen    /** Resolved canonical path to legacy storage. */
1315943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen    private static final String sLegacyPath;
132007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
133007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    static {
134007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
135e2c8247857cd266650a237b26c74a19bda915ea8Marco Nelissen            sExternalPath =
136e2c8247857cd266650a237b26c74a19bda915ea8Marco Nelissen                    Environment.getExternalStorageDirectory().getCanonicalPath() + File.separator;
137e2c8247857cd266650a237b26c74a19bda915ea8Marco Nelissen            sCachePath =
138e2c8247857cd266650a237b26c74a19bda915ea8Marco Nelissen                    Environment.getDownloadCacheDirectory().getCanonicalPath() + File.separator;
139e2c8247857cd266650a237b26c74a19bda915ea8Marco Nelissen            sLegacyPath =
140e2c8247857cd266650a237b26c74a19bda915ea8Marco Nelissen                    Environment.getLegacyExternalStorageDirectory().getCanonicalPath()
141e2c8247857cd266650a237b26c74a19bda915ea8Marco Nelissen                    + File.separator;
142007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } catch (IOException e) {
143007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throw new RuntimeException("Unable to resolve canonical paths", e);
144007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
145007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
146007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
1475d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey    private StorageManager mStorageManager;
1485d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey
1497f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    // In memory cache of path<->id mappings, to speed up inserts during media scan
1507f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>();
1517f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
1528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
1538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
1548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
1558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
1568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
1578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
15820434e032e498b716f87cce2f23dd646819218bfRay Chen    // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest.
15920434e032e498b716f87cce2f23dd646819218bfRay Chen    private MediaThumbRequest mCurrentThumbRequest = null;
160b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
161b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
162b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
163b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
164f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood    private boolean mCaseInsensitivePaths;
1659be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private static String[] mExternalStoragePaths;
16617ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood
167a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
168a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
169a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
170a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
171a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
172a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
173a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
174a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
175a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
176a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
177a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
178a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
179a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
180a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
181a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
182a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
183a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
184a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
185ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
186ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
187a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
188a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
189a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
190a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
191a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
192a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
193a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
194a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
195a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
196a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
19763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
198a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
199a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
200a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
201a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
202a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
203a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
204a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
205a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
206a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
20763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
20863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
209e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen            " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" +
21063f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
211a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
212a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
21363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
21463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
215a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
216a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTableColumns = new String[] {
21716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            FileColumns._ID,
218afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            FileColumns.MEDIA_TYPE,
2191717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    };
2201717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
2217f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    private static final String[] sIdOnlyColumn = new String[] {
2227f36494e085c26c69cd5925e54028822025eff29Marco Nelissen        FileColumns._ID
2237f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    };
2247f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
225166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    private static final String[] sDataOnlyColumn = new String[] {
226166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        FileColumns.DATA
227166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    };
228166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
229a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTypeDataId = new String[] {
230a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.MEDIA_TYPE,
231a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.DATA,
232a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns._ID
2334eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    };
2344eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen
2354eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    private static final String[] sPlaylistIdPlayOrder = new String[] {
2364eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen        Playlists.Members.PLAYLIST_ID,
2374eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen        Playlists.Members.PLAY_ORDER
2384eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    };
239a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
240a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
241a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
24201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    private static final String CANONICAL = "canonical";
24301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
247f5510c0040fbe0ee6ba1d1f24b6f760fdc6ca5b9Nick Kralevich            if (Intent.ACTION_MEDIA_EJECT.equals(intent.getAction())) {
2481f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                StorageVolume storage = (StorageVolume)intent.getParcelableExtra(
2491f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        StorageVolume.EXTRA_STORAGE_VOLUME);
2501f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // If primary external storage is ejected, then remove the external volume
2511f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // notify all cursors backed by data on that volume.
2521f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                if (storage.getPath().equals(mExternalStoragePaths[0])) {
2531f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    detachVolume(Uri.parse("content://media/external"));
2541f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    sFolderArtMap.clear();
2551f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    MiniThumbFile.reset();
2561f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                } else {
2571f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // If secondary external storage is ejected, then we delete all database
2581f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // entries for that storage from the files table.
2596f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                    DatabaseHelper database;
2601f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    synchronized (mDatabases) {
2616f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                        // This synchronized block is limited to avoid a potential deadlock
2626f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                        // with bulkInsert() method.
2636f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                        database = mDatabases.get(EXTERNAL_VOLUME);
2646f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                    }
2656f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                    Uri uri = Uri.parse("file://" + storage.getPath());
2666f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                    if (database != null) {
2676f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                        try {
2686f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            // Send media scanner started and stopped broadcasts for apps that rely
2696f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            // on these Intents for coarse grained media database notifications.
2706f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            context.sendBroadcast(
2716f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                                    new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
2721f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood
2736f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            // don't send objectRemoved events - MTP be sending StorageRemoved anyway
2746f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            mDisableMtpObjectCallbacks = true;
2756f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            Log.d(TAG, "deleting all entries for storage " + storage);
2766f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            SQLiteDatabase db = database.getWritableDatabase();
2776f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            // First clear the file path to disable the _DELETE_FILE database hook.
2786f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            // We do this to avoid deleting files if the volume is remounted while
2796f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            // we are still processing the unmount event.
2806f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            ContentValues values = new ContentValues();
281405787926f417dc8609b3b9944699c01ffde874bKenny Root                            values.putNull(Files.FileColumns.DATA);
2826f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            String where = FileColumns.STORAGE_ID + "=?";
2836f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) };
2846f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            database.mNumUpdates++;
2856f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            db.update("files", values, where, whereArgs);
2866f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            // now delete the records
2876f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            database.mNumDeletes++;
2886f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            int numpurged = db.delete("files", where, whereArgs);
2896f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            logToDb(db, "removed " + numpurged +
2906f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                                    " rows for ejected filesystem " + storage.getPath());
2916f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            // notify on media Uris as well as the files Uri
2926f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            context.getContentResolver().notifyChange(
2936f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                                    Audio.Media.getContentUri(EXTERNAL_VOLUME), null);
2946f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            context.getContentResolver().notifyChange(
2956f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                                    Images.Media.getContentUri(EXTERNAL_VOLUME), null);
2966f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            context.getContentResolver().notifyChange(
2976f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                                    Video.Media.getContentUri(EXTERNAL_VOLUME), null);
2986f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            context.getContentResolver().notifyChange(
2996f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                                    Files.getContentUri(EXTERNAL_VOLUME), null);
3006f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                        } catch (Exception e) {
3016f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            Log.e(TAG, "exception deleting storage entries", e);
3026f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                        } finally {
3036f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            context.sendBroadcast(
3046f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                                    new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
3056f52075284b9f3ffac72f0118ada12f462a378b1Sangkyu Lee                            mDisableMtpObjectCallbacks = false;
3061f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        }
3071f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    }
3081f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                }
309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
313d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    // set to disable sending events when the operation originates from MTP
314d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private boolean mDisableMtpObjectCallbacks;
315d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
316d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final SQLiteDatabase.CustomFunction mObjectRemovedCallback =
317d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                new SQLiteDatabase.CustomFunction() {
318d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void callback(String[] args) {
3197f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // We could remove only the deleted entry from the cache, but that
3207f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // requires the path, which we don't have here, so instead we just
3217f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // clear the entire cache.
3227f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: include the path in the callback and only remove the affected
3237f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // entry from the cache
3247f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            mDirectoryCache.clear();
325d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // do nothing if the operation originated from MTP
326d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mDisableMtpObjectCallbacks) return;
327d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
328d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "object removed " + args[0]);
329d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            IMtpService mtpService = mMtpService;
330d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mtpService != null) {
331d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                try {
332d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    sendObjectRemoved(Integer.parseInt(args[0]));
333d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                } catch (NumberFormatException e) {
334d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e);
335d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
336d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
337d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
338d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
339d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
345fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static final class DatabaseHelper extends SQLiteOpenHelper {
346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
3475524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        final String mName;
348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
349fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final boolean mEarlyUpgrade;
350fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final SQLiteDatabase.CustomFunction mObjectRemovedCallback;
3515524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        boolean mUpgradeAttempted; // Used for upgrade error handling
35210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumQueries;
35310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumUpdates;
35410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumInserts;
35510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumDeletes;
35610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStartTime;
35710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStopTime;
358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
363fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        public DatabaseHelper(Context context, String name, boolean internal,
364fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                boolean earlyUpgrade,
365fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                SQLiteDatabase.CustomFunction objectRemovedCallback) {
36690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            super(context, name, null, getDatabaseVersion(context));
367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
3685524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mName = name;
369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
370fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mEarlyUpgrade = earlyUpgrade;
371fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mObjectRemovedCallback = objectRemovedCallback;
3720e2a2386b39972286df21f4db5a9dd1df548c34dJeff Brown            setWriteAheadLoggingEnabled(true);
373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
38090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, 0, getDatabaseVersion(mContext));
381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
3895524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = true;
39090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, oldV, newV);
391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
393db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        @Override
394db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        public synchronized SQLiteDatabase getWritableDatabase() {
3955524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            SQLiteDatabase result = null;
3965524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = false;
3975524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            try {
3985524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3995524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            } catch (Exception e) {
4005524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                if (!mUpgradeAttempted) {
4015524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    Log.e(TAG, "failed to open database " + mName, e);
4025524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    return null;
4035524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                }
4045524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
4055524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
4065524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // If we failed to open the database during an upgrade, delete the file and try again.
4075524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // This will result in the creation of a fresh database, which will be repopulated
4085524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // when the media scanner runs.
4095524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            if (result == null && mUpgradeAttempted) {
410450d884f1bd5de323a645ce1acfae40fb91b8cb0jangwon.lee                mContext.deleteDatabase(mName);
4115524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
4125524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
4135524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            return result;
4145524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        }
4155524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
417993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * For devices that have removable storage, we support keeping multiple databases
418993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * to allow users to switch between a number of cards.
419993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * On such devices, touch this particular database and garbage collect old databases.
420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
42536d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky
426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
428fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mEarlyUpgrade) return; // Doing early upgrade.
429fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
430fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mObjectRemovedCallback != null) {
431fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback);
432fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            }
433d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
434993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            // the code below is only needed on devices with removable storage
435993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            if (!Environment.isExternalStorageRemovable()) return;
436993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
444d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang            // Don't delete wal auxiliary files(db-shm and db-wal) directly because db file may
445d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang            // not be deleted, and it will cause Disk I/O error when accessing this database.
446d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang            List<String> dbList = new ArrayList<String>();
447d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang            for (String database : databases) {
448d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang                if (database != null && database.endsWith(".db")) {
449d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang                    dbList.add(database);
450d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang                }
451d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang            }
452d1b7c58a1c14229873987af728c4c598f86bd107Benson Huang            databases = dbList.toArray(new String[0]);
453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
50634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood    // synchronize on mMtpServiceConnection when accessing mMtpService
507d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private IMtpService mMtpService;
508d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
509d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final ServiceConnection mMtpServiceConnection = new ServiceConnection() {
510d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood         public void onServiceConnected(ComponentName className, android.os.IBinder service) {
51134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
51234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = IMtpService.Stub.asInterface(service);
51334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
514d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
515d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
516d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void onServiceDisconnected(ComponentName className) {
51734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
51834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = null;
51934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
520d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
521d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
522d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
523ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    private static final String[] sDefaultFolderNames = {
524ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MUSIC,
525ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PODCASTS,
526ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_RINGTONES,
527ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_ALARMS,
528ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_NOTIFICATIONS,
529ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PICTURES,
530ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MOVIES,
531ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DOWNLOADS,
532ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DCIM,
533ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    };
534ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
535ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    // creates default folders (Music, Downloads, etc)
53610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void createDefaultFolders(DatabaseHelper helper, SQLiteDatabase db) {
537ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // Use a SharedPreference to ensure we only do this once.
538ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // We don't want to annoy the user by recreating the directories
539ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // after she has deleted them.
540ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
541ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        if (prefs.getInt("created_default_folders", 0) == 0) {
542ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            for (String folderName : sDefaultFolderNames) {
543ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                File file = Environment.getExternalStoragePublicDirectory(folderName);
544ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                if (!file.exists()) {
545ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                    file.mkdirs();
54610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    insertDirectory(helper, db, file.getAbsolutePath());
547ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                }
548ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            }
549ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
550ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            SharedPreferences.Editor e = prefs.edit();
551ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.clear();
552ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.putInt("created_default_folders", 1);
553ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.commit();
554ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        }
555ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
556ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
5579311c7ff9d35ca3acc908da3da7a79fbf7a8da6bMarco Nelissen    public static int getDatabaseVersion(Context context) {
55890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        try {
55990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            return context.getPackageManager().getPackageInfo(
56090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    context.getPackageName(), 0).versionCode;
56190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        } catch (NameNotFoundException e) {
56290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            throw new RuntimeException("couldn't get version code for " + context);
56390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
56490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    }
56590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen
566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
568d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        final Context context = getContext();
569d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
5705d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey        mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
5715d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey
572acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
573acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums._ID);
574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
576acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
578acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
583acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
584acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
585acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
586acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.ALBUM_ART);
587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
58863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
58963f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
590d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "%1", context.getString(R.string.artist_label));
591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
596d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        context.registerReceiver(mUnmountReceiver, iFilter);
597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
598c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        StorageManager storageManager =
599c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood                (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
600c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        mExternalStoragePaths = storageManager.getVolumePaths();
6019be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
609ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND);
610ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        ht.start();
611ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        mThumbHandler = new Handler(ht.getLooper()) {
612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
614b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
615b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
61620434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest = mMediaThumbQueue.poll();
617b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
61820434e032e498b716f87cce2f23dd646819218bfRay Chen                    if (mCurrentThumbRequest == null) {
619b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
620b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
621b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
622d1f37448350890725736ababcc23c7deb0b2153fleozwang                            if (mCurrentThumbRequest.mPath != null) {
623d1f37448350890725736ababcc23c7deb0b2153fleozwang                                File origFile = new File(mCurrentThumbRequest.mPath);
624d1f37448350890725736ababcc23c7deb0b2153fleozwang                                if (origFile.exists() && origFile.length() > 0) {
625d1f37448350890725736ababcc23c7deb0b2153fleozwang                                    mCurrentThumbRequest.execute();
626d1f37448350890725736ababcc23c7deb0b2153fleozwang                                    // Check if more requests for the same image are queued.
627d1f37448350890725736ababcc23c7deb0b2153fleozwang                                    synchronized (mMediaThumbQueue) {
628d1f37448350890725736ababcc23c7deb0b2153fleozwang                                        for (MediaThumbRequest mtq : mMediaThumbQueue) {
629d1f37448350890725736ababcc23c7deb0b2153fleozwang                                            if ((mtq.mOrigId == mCurrentThumbRequest.mOrigId) &&
630d1f37448350890725736ababcc23c7deb0b2153fleozwang                                                (mtq.mIsVideo == mCurrentThumbRequest.mIsVideo) &&
631d1f37448350890725736ababcc23c7deb0b2153fleozwang                                                (mtq.mMagic == 0) &&
632d1f37448350890725736ababcc23c7deb0b2153fleozwang                                                (mtq.mState == MediaThumbRequest.State.WAIT)) {
633d1f37448350890725736ababcc23c7deb0b2153fleozwang                                                mtq.mMagic = mCurrentThumbRequest.mMagic;
634d1f37448350890725736ababcc23c7deb0b2153fleozwang                                            }
635318b67923bb9ae30177aee872f5f7f9973b900ecBjörn Davidsson                                        }
636318b67923bb9ae30177aee872f5f7f9973b900ecBjörn Davidsson                                    }
637d1f37448350890725736ababcc23c7deb0b2153fleozwang                                } else {
638d1f37448350890725736ababcc23c7deb0b2153fleozwang                                    // original file hasn't been stored yet
639d1f37448350890725736ababcc23c7deb0b2153fleozwang                                    synchronized (mMediaThumbQueue) {
640d1f37448350890725736ababcc23c7deb0b2153fleozwang                                        Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath);
641d1f37448350890725736ababcc23c7deb0b2153fleozwang                                    }
6424d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
6434d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
644b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
6451d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
6461d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                        } catch (UnsupportedOperationException ex) {
6471d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // This could happen if we unplug the sd card during insert/update/delete
6481d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // See getDatabaseForUri.
6491d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
65022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                        } catch (OutOfMemoryError err) {
65122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            /*
65222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Note: Catching Errors is in most cases considered
65322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * bad practice. However, in this case it is
65422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * motivated by the fact that corrupt or very large
65522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * images may cause a huge allocation to be
65622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * requested and denied. The bitmap handling API in
65722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Android offers no other way to guard against
65822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * these problems than by catching OutOfMemoryError.
65922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             */
66022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            Log.w(TAG, err);
661b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
66220434e032e498b716f87cce2f23dd646819218bfRay Chen                            synchronized (mCurrentThumbRequest) {
66320434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE;
66420434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.notifyAll();
665b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
666b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
667b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
668b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
669b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
670b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
671b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
672b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
674d63eb65db514ad7064951f221f0278a3f2293411Jeff Sharkey                    IoUtils.closeQuietly(makeThumbInternal(d));
675b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
676b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
677b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
685395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_FILES = "files";
686395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_ALBUM_ART = "album_art";
687395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_THUMBNAILS = "thumbnails";
688395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_VIDEO_THUMBNAILS = "videothumbnails";
689395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
690afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String IMAGE_COLUMNS =
691afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_size,_display_name,mime_type,title,date_added," +
692afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
693bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name," +
694bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "width,height";
695bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
696bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String IMAGE_COLUMNSv407 =
697bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_size,_display_name,mime_type,title,date_added," +
698bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
699afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name";
700afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
701805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv99 =
702afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added," +
703afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
704afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
705afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark";
706afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
707805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv100 =
708805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "_data,_display_name,_size,mime_type,date_added," +
709805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
710805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
711805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "bookmark,album_artist";
712805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen
713957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang    private static final String AUDIO_COLUMNSv405 =
714957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "_data,_display_name,_size,mime_type,date_added,is_drm," +
715957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
716957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
717957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "bookmark,album_artist";
718957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
719afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String VIDEO_COLUMNS =
720afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
721afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title,duration,artist,album,resolution,description,isprivate,tags," +
722afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
723bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width," +
724bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "height";
725bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
726bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String VIDEO_COLUMNSv407 =
727bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
728bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "title,duration,artist,album,resolution,description,isprivate,tags," +
729bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
730afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic,bucket_id,bucket_display_name, bookmark";
731afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
732afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified";
733afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
74290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    private static void updateDatabase(Context context, SQLiteDatabase db, boolean internal,
743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
74690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        int dbversion = getDatabaseVersion(context);
74790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (toVersion != dbversion) {
74890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " + dbversion);
749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
75195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
755988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        long startTime = SystemClock.currentTimeMicro();
756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
757d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag.
758acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        // We can't downgrade from those revisions, so start over.
759022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // (the initial change to do this was wrong, so now we actually need to start over
760022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // if the database version is 84-89)
761bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair.
762bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // However version 91 was reused in a divergent development path for gingerbread,
763bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // so we need to support upgrades from 91.
764bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Therefore we will only force a reset for versions 92 - 94.
765d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) ||
766bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    (fromVersion >= 92 && fromVersion <= 94)) {
767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
770d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            fromVersion = 63;
771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
793cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
7949ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup");
7959ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup");
7969ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup");
7979ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup");
798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
828b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
848216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                       "_data TEXT UNIQUE NOT NULL," +
849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
88895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
894acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album) AS number_of_albums, " +
895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
908acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
909acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
910acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
911acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
912acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
93078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                // Contains mappings between audio genres and audio files
931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
1000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
1001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
1002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
1003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
1004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
1005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
1007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
1008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
1009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
1010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
1013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
1014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
1015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
1017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
1018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
1019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1021acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1022acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.0 shipped with database version 64
1023acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1024acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
1026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
1027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
1028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1030403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // In version 66, originally we updateBucketNames(db, "images"),
1031403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // but we need to do it in version 89 and therefore save the update here.
1032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
1034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
1035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
1036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
1037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
1040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
1041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
1042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
1043403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1044403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // In version 68, originally we updateBucketNames(db, "video"),
1045403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // but we need to do it in version 89 and therefore save the update here.
1046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
1049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
1050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
1053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
1054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
1055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
105695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
1058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
1059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
1060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
1061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
1062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
1063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
1064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
1065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
1066e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "artist='" + MediaStore.UNKNOWN_STRING + "' AND " +
1067e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "album='" + MediaStore.UNKNOWN_STRING + "'" +
1068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
1069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
107095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1071702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
1072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
1073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
1074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
1075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
1076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
1077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
1078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
1080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
1081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
1082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
1083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
1084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
108595ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1086acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1087acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.5 shipped with database version 72
1088acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1089acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
10908d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
10918d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
10928d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
10938d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
10948d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
10958d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
10968d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
10978d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
10988d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
10998d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
11008d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
11018d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
11028d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
11038d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
11048d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
1105a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1106a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
1107a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
1108a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
1109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
1110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
1111a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
1112a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
1113a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1114a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
1115a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1116a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
1117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1118a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
1119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
1121a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
1122a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
1123a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
1124a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
1125a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
1126a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
1127e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " +
1128a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1129a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1130a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
1131a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1132a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1133a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1134a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
1135a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1136a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1137a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1138a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
1139a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
1140a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
1141e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " +
1142a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1143a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
1144a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
1145a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1146a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1147a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
1148a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
1149a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1150a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1151a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1152a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
1153a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
1154a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
1155a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
1156a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
1157a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
1158a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
115959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
116059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
116195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
116259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
116359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
116459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
116559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
116615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
116715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
116815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
116915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
117015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
117115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
117215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
117315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
117415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
117515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
117615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
117715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
117815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
117915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
1180b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1181acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1182acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.6 shipped with database version 76
1183acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1184acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1185b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
1186b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
1187b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
1188b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
1189b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
1190b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
1191b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
1192b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
1193b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
1194b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
1195b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1196b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
1197b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1198b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
1199b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
1200b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
1201b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
1202b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
12031769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen
1204acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1205acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.0 and 2.0.1 shipped with database version 77
1206acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1207acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
12081769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        if (fromVersion < 78) {
1209044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force a rescan of the video entries so we can update
12101769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            // latest changed DATE_TAKEN units (in milliseconds).
12111769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            db.execSQL("UPDATE video SET date_modified=0;");
12121769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        }
1213268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
1214acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1215acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.1 shipped with database version 78
1216acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1217acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1218268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        if (fromVersion < 79) {
1219268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // move /sdcard/albumthumbs to
1220268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // /sdcard/Android/data/com.android.providers.media/albumthumbs,
1221268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // and update the database accordingly
1222268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
12239be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String oldthumbspath = mExternalStoragePaths[0] + "/albumthumbs";
12249be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String newthumbspath = mExternalStoragePaths[0] + "/" + ALBUM_THUMB_FOLDER;
1225268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            File thumbsfolder = new File(oldthumbspath);
1226268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            if (thumbsfolder.exists()) {
1227268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                // move folder to its new location
1228268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                File newthumbsfolder = new File(newthumbspath);
1229268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                newthumbsfolder.getParentFile().mkdirs();
1230268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                if(thumbsfolder.renameTo(newthumbsfolder)) {
1231268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    // update the database
1232268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" +
1233268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                            oldthumbspath + "','" + newthumbspath + "');");
1234268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                }
1235268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            }
1236268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        }
1237044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen
1238044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        if (fromVersion < 80) {
1239044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force rescan of image entries to update DATE_TAKEN as UTC timestamp.
1240044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            db.execSQL("UPDATE images SET date_modified=0;");
1241044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        }
12420ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
1243166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen        if (fromVersion < 81 && !internal) {
12440ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete entries starting with /mnt/sdcard. This is for the benefit
12450ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // of users running builds between 2.0.1 and 2.1 final only, since
12460ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // users updating from 2.0 or earlier will not have such entries.
12470ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12480ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // First we need to update the _data fields in the affected tables, since
12490ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // otherwise deleting the entries will also delete the underlying files
12500ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // (via a trigger), and we want to keep them.
12510ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12520ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12530ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12540ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12550ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12560ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
1257216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12580ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Once the paths have been renamed, we can safely delete the entries
12590ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';");
12600ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM images WHERE _data IS '////';");
12610ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM video WHERE _data IS '////';");
12620ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';");
12630ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';");
12640ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_meta WHERE _data  IS '////';");
12650ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data  IS '////';");
12660ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12670ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // rename existing entries starting with /sdcard to /mnt/sdcard
12680ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta" +
12690ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12700ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists" +
12710ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12720ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images" +
12730ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12740ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video" +
12750ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12760ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails" +
12770ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12780ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails" +
12790ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12800ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art" +
12810ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12820ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12830ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete albums and artists, then clear the modification time on songs, which
12840ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // will cause the media scanner to rescan everything, rebuilding the artist and
12850ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // album tables along the way, while preserving playlists.
12860ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // We need this rescan because ICU also changed, and now generates different
12870ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // collation keys
12880ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from albums");
12890ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from artists");
12900ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
12910ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen        }
129284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen
129384403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        if (fromVersion < 82) {
1294acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // recreate this view with the correct "group by" specifier
129584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("DROP VIEW IF EXISTS artist_info");
129684403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
129784403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "SELECT artist_id AS _id, artist, artist_key, " +
1298acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album_key) AS number_of_albums, " +
129984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
130084403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "GROUP BY artist_key;");
130184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        }
1302216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1303acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /* we skipped over version 83, and reverted versions 84, 85 and 86 */
1304ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen
1305ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        if (fromVersion < 87) {
1306ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // The fastscroll thumb needs an index on the strings being displayed,
1307ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // otherwise the queries it does to determine the correct position
1308ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // becomes really inefficient
1309022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);");
1310022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);");
1311022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);");
1312ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        }
1313216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1314acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        if (fromVersion < 88) {
1315acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // Clean up a few more things from versions 84/85/86, and recreate
1316acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // the few things worth keeping from those changes.
1317acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update1;");
1318acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update2;");
1319acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update3;");
1320acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update4;");
1321acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update1;");
1322acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update2;");
1323acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update3;");
1324acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update4;");
132516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP VIEW IF EXISTS album_artists;");
1326acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);");
1327acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);");
1328acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
1329acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
1330acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
1331acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
1332acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        }
1333403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1334fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // In version 89, originally we updateBucketNames(db, "images") and
1335fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames
1336fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        //  for all files and therefore can save the update here.
1337b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1338b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (fromVersion < 91) {
1339bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // Never query by mini_thumb_magic_index
1340bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index");
1341bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1342bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // sort the items by taken date in each bucket
1343bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)");
1344bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)");
1345bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        }
1346bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1347a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
1348d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // Gingerbread ended up going to version 100, but didn't yet have the "files"
1349d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // table, so we need to create that if we're at 100 or lower. This means
1350d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // we won't be able to upgrade pre-release Honeycomb.
1351d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        if (fromVersion <= 100) {
1352afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Remove various stages of work in progress for MTP support
1353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
1354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS files");
135516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;");
135616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;");
135716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;");
135816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;");
1359afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;");
1360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;");
1361afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;");
1362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;");
1363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;");
1364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1365afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create a new table to manage all files in our storage.
1366afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // This contains a union of all the columns from the old
1367afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // images, audio_meta, videos and audio_playlist tables.
1368afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TABLE files (" +
136900a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen                        "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1370afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data TEXT," +     // this can be null for playlists
1371afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_size INTEGER," +
1372afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "format INTEGER," +
1373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "parent INTEGER," +
1374afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_added INTEGER," +
1375afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified INTEGER," +
1376afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mime_type TEXT," +
1377afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title TEXT," +
1378afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "description TEXT," +
1379afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_display_name TEXT," +
1380afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images
1382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "picasa_id TEXT," +
1383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "orientation INTEGER," +
1384afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1385afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images and video
1386afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "latitude DOUBLE," +
1387afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "longitude DOUBLE," +
1388afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken INTEGER," +
1389afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic INTEGER," +
1390afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_id TEXT," +
1391afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_display_name TEXT," +
1392afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "isprivate INTEGER," +
1393afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1394afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio
1395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title_key TEXT," +
1396afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist_id INTEGER," +
1397afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album_id INTEGER," +
1398afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "composer TEXT," +
1399afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track INTEGER," +
1400afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "year INTEGER CHECK(year!=0)," +
1401afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_ringtone INTEGER," +
1402afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_music INTEGER," +
1403afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_alarm INTEGER," +
1404afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_notification INTEGER," +
1405afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_podcast INTEGER," +
1406d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        "album_artist TEXT," +
1407afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1408afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio and video
1409afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "duration INTEGER," +
1410afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark INTEGER," +
1411afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1412afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for video
1413afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist TEXT," +
1414afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album TEXT," +
1415afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "resolution TEXT," +
1416afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "tags TEXT," +
1417afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category TEXT," +
1418afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "language TEXT," +
1419afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_data TEXT," +
1420afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1421afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for playlists
1422afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "name TEXT," +
1423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1424afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // media_type is used by the views to emulate the old
1425afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // images, audio_meta, videos and audio_playlist tables.
1426afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "media_type INTEGER," +
1427afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Value of _id from the old media table.
1429afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Used only for updating other tables during database upgrade.
1430afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "old_id INTEGER" +
1431afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       ");");
1432d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen
1433afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX path_index ON files(_data);");
1434afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1435afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1436afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Copy all data from our obsolete tables to the new files table
143792be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
143892be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Copy audio records first, preserving the _id column.
143992be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We do this to maintain compatibility for content Uris for ringtones.
144092be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Unfortunately we cannot do this for images and videos as well.
144192be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We choose to do this for the audio table because the fragility of Uris
144292be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // for ringtones are the most common problem we need to avoid.
144392be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            db.execSQL("INSERT INTO files (_id," + AUDIO_COLUMNSv99 + ",old_id,media_type)" +
144492be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " SELECT _id," + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO +
144592be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " FROM audio_meta;");
144692be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
1447bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + IMAGE_COLUMNSv407 + ",old_id,media_type) SELECT "
1448bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + IMAGE_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;");
1449bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + VIDEO_COLUMNSv407 + ",old_id,media_type) SELECT "
1450bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + VIDEO_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;");
145116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            if (!internal) {
1452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT "
1453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST
1454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + " FROM audio_playlists;");
1455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
145616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1457afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Delete the old tables
1458afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS images");
1459afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_meta");
1460afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS video");
1461afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
146216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1463afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create views to replace our old tables
1464bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNSv407 +
1465afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1466afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1467d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 +
1468d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1469d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1470bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNSv407 +
1471afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1472afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1474afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS +
1475afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1476afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_PLAYLIST + ";");
147716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            }
147836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
14799491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // create temporary index to make the updates go faster
14809491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("CREATE INDEX tmp ON files(old_id);");
14819491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1482afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update the image_id column in the thumbnails table.
1483afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files "
1484afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = thumbnails.image_id AND files.media_type = "
1485afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ");");
148636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1487afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1488d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // update audio_id in the audio_genres_map table, and
1489d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // audio_playlists_map tables and playlist_id in the audio_playlists_map table
1490afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files "
1491afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = "
1492afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_AUDIO + ");");
1493afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files "
1494afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_playlists_map.audio_id "
1495afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");");
1496d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET playlist_id = (SELECT _id FROM files "
1497d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "WHERE files.old_id = audio_playlists_map.playlist_id "
1498d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + ");");
1499afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
1500afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1501afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update video_id in the videothumbnails table.
1502afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files "
1503afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = "
1504afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ");");
1505afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
15069491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // we don't need this index anymore now
15079491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("DROP INDEX tmp;");
15089491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1509afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update indices to work on the files table
1510afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS title_idx");
1511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS album_id_idx");
1512afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS image_bucket_index");
1513afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS video_bucket_index");
1514afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS sort_index");
1515afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS titlekey_index");
1516afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS artist_id_idx");
1517afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX title_idx ON files(title);");
1518afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1519afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);");
1520afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1521afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1522afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1523afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1524afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Recreate triggers for our obsolete tables on the new files table
1525afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
1526afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
1527afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
1528afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
1529afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
153036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1531afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " +
1532afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " +
153336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1534afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
1535afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
153636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
153736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1538afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " +
153949dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " +
154036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1541afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
154236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
154336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1544afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1545afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " +
1546afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " +
1547afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1548afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
1549afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
1550afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1551afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1552afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " +
1553afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " +
1554afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1555afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1556afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "SELECT _DELETE_FILE(old._data);" +
1557afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1558afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1559afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
156036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "BEGIN " +
1561afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from files where _id=old._id;" +
1562afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_playlists_map where audio_id=old._id;" +
1563afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_genres_map where audio_id=old._id;" +
156436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "END");
156536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood            }
156636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood        }
156736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1568db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (fromVersion < 301) {
1569db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("DROP INDEX IF EXISTS bucket_index");
1570db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)");
1571db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)");
1572db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
1573db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin
157420405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        if (fromVersion < 302) {
157520405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX parent_index ON files(parent);");
157620405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX format_index ON files(format);");
157720405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        }
157820405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood
15792658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        if (fromVersion < 303) {
15802658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // the album disambiguator hash changed, so rescan songs and force
15812658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // albums to be updated. Artists are unaffected.
15822658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("DELETE from albums");
15832658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
15842658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
15852658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        }
15862658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
15874b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 304 && !internal) {
158851d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            // notifies host when files are deleted
158951d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
159051d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "BEGIN " +
159151d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                        "SELECT _OBJECT_REMOVED(old._id);" +
159251d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "END");
159351d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
159451d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood        }
159551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
15964b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 305 && internal) {
15974b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            // version 304 erroneously added this trigger to the internal database
15984b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
15994b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        }
16004b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood
1601fda522dc66b94057f9c6676cb8ba10bc3b13daeaMarco Nelissen        if (fromVersion < 306 && !internal) {
1602efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // The genre list was expanded and genre string parsing was tweaked, so
1603efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // rebuild the genre list
1604efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
1605efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
1606efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
1607efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
1608efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen        }
1609efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen
161065587f9c204abb2d179527dfdca009f4780e9743Ray Chen        if (fromVersion < 307 && !internal) {
161165587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // Force rescan of image entries to update DATE_TAKEN by either GPSTimeStamp or
161265587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // EXIF local time.
161365587f9c204abb2d179527dfdca009f4780e9743Ray Chen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
161465587f9c204abb2d179527dfdca009f4780e9743Ray Chen                    + FileColumns.MEDIA_TYPE_IMAGE + ";");
161565587f9c204abb2d179527dfdca009f4780e9743Ray Chen        }
161665587f9c204abb2d179527dfdca009f4780e9743Ray Chen
1617e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // Honeycomb went up to version 307, ICS started at 401
1618e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
16194f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // Database version 401 did not add storage_id to the internal database.
16204f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // We need it there too, so add it in version 402
16214f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        if (fromVersion < 401 || (fromVersion == 401 && internal)) {
16229be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Add column for MTP storage ID
16239be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("ALTER TABLE files ADD COLUMN storage_id INTEGER;");
16249be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Anything in the database before this upgrade step will be in the primary storage
16259be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("UPDATE files SET storage_id=" + MtpStorage.getStorageId(0) + ";");
16269be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
16279be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
162878b2885edc406273d688536b0eadfea006b20662Marco Nelissen        if (fromVersion < 403 && !internal) {
162978b2885edc406273d688536b0eadfea006b20662Marco Nelissen            db.execSQL("CREATE VIEW audio_genres_map_noid AS " +
163078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    "SELECT audio_id,genre_id from audio_genres_map;");
163178b2885edc406273d688536b0eadfea006b20662Marco Nelissen        }
163278b2885edc406273d688536b0eadfea006b20662Marco Nelissen
16339289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        if (fromVersion < 404) {
16349289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // There was a bug that could cause distinct same-named albums to be
16359289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // combined again. Delete albums and force a rescan.
16369289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("DELETE from albums");
16379289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16389289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
16399289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        }
16409289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen
1641957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        if (fromVersion < 405) {
1642957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            // Add is_drm column.
1643957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("ALTER TABLE files ADD COLUMN is_drm INTEGER;");
1644957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1645957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("DROP VIEW IF EXISTS audio_meta");
1646957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv405 +
1647957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1648957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1649957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1650957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            recreateAudioView(db);
1651957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        }
1652957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1653b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (fromVersion < 407) {
16547ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            // Rescan files in the media database because a new column has been added
1655b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // in table files in version 405 and to recover from problems populating
1656b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // the genre tables
16577ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            db.execSQL("UPDATE files SET date_modified=0;");
16587ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang        }
16597ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang
1660bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        if (fromVersion < 408) {
1661bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Add the width/height columns for images and video
1662bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN width INTEGER;");
1663bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN height INTEGER;");
1664bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1665bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Rescan files to fill the columns
1666bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("UPDATE files SET date_modified=0;");
1667bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1668bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Update images and video views to contain the width/height columns
1669bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS images");
1670bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS video");
1671bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS +
1672bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1673bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1674bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS +
1675bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1676bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1677bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        }
1678bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
16795809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        if (fromVersion < 409 && !internal) {
16805809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // A bug that prevented numeric genres from being parsed was fixed, so
16815809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // rebuild the genre list
16825809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16835809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
16845809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
16855809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
16865809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        }
16875809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen
1688e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // ICS went out with database version 409, JB started at 500
1689e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1690166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        if (fromVersion < 500) {
1691166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1692166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS videothumbnails_cleanup;");
1693166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        }
1694a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        if (fromVersion < 501) {
1695a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1696a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // the images_cleanup trigger would delete the image file and the entry
1697a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // in the thumbnail table, which in turn would trigger thumbnails_cleanup
1698a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // to delete the thumbnail image
1699a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup;");
1700a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup;");
1701a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        }
17025afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        if (fromVersion < 502) {
17035afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
17045afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup;");
17055afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        }
17065118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        if (fromVersion < 503) {
17075118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            // genre and playlist cleanup now done in mediaprovider code, instead of in a trigger
17085118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
17095118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
17105118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        }
171190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (fromVersion < 504) {
171290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            // add an index to help with case-insensitive matching of paths
171390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            db.execSQL(
171490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    "CREATE INDEX IF NOT EXISTS path_index_lower ON files(_data COLLATE NOCASE);");
171590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
17167074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen        if (fromVersion < 505) {
17177074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            // Starting with schema 505 we fill in the width/height/resolution columns for videos,
17187074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            // so force a rescan of videos to fill in the blanks
17197074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
17207074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen                    + FileColumns.MEDIA_TYPE_VIDEO + ";");
17217074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen        }
172259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen        if (fromVersion < 506) {
172359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // sd card storage got moved to /storage/sdcard0
172459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // first delete everything that already got scanned in /storage before this
172559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // update step was added
172659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
172759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM files WHERE _data LIKE '/storage/%';");
172859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data LIKE '/storage/%';");
172959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data LIKE '/storage/%';");
173059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data LIKE '/storage/%';");
173159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // then rename everything from /mnt/sdcard/ to /storage/sdcard0,
173259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // and from /mnt/external1 to /storage/sdcard1
173359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE files SET " +
173459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
173559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE files SET " +
173659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
173759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE album_art SET " +
173859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
173959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE album_art SET " +
174059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
174159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE thumbnails SET " +
174259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
174359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE thumbnails SET " +
174459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
174559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE videothumbnails SET " +
174659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
174759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE videothumbnails SET " +
174859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
174959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen
175059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            if (!internal) {
175159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
175259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                    "BEGIN " +
175359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                        "SELECT _OBJECT_REMOVED(old._id);" +
175459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                    "END");
175559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            }
175659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen        }
1757a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin        if (fromVersion < 507) {
1758a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin            // we update _data in version 506, we need to update the bucket_id as well
1759677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            updateBucketNames(db);
1760a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin        }
176146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen        if (fromVersion < 508 && !internal) {
176246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            // ensure we don't get duplicate entries in the genre map
176346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map_tmp (" +
176446e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "_id INTEGER PRIMARY KEY," +
176546e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "audio_id INTEGER NOT NULL," +
176646e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "genre_id INTEGER NOT NULL," +
176746e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "UNIQUE (audio_id,genre_id) ON CONFLICT IGNORE" +
176846e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    ");");
176946e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("INSERT INTO audio_genres_map_tmp (audio_id,genre_id)" +
177046e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    " SELECT DISTINCT audio_id,genre_id FROM audio_genres_map;");
177146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("DROP TABLE audio_genres_map;");
177246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("ALTER TABLE audio_genres_map_tmp RENAME TO audio_genres_map;");
177346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen        }
1774988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1775988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        if (fromVersion < 509) {
1776988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS log (time DATETIME PRIMARY KEY, message TEXT);");
1777988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        }
1778395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1779395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        // Emulated external storage moved to user-specific paths
1780395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        if (fromVersion < 510 && Environment.isExternalStorageEmulated()) {
1781395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            // File.fixSlashes() removes any trailing slashes
1782395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            final String externalStorage = Environment.getExternalStorageDirectory().toString();
178357b65f10aaafc4bf42a0fa59eb2bbe6c8371c2e6Jeff Sharkey            Log.d(TAG, "Adjusting external storage paths to: " + externalStorage);
1784395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1785395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            final String[] tables = {
1786395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                    TABLE_FILES, TABLE_ALBUM_ART, TABLE_THUMBNAILS, TABLE_VIDEO_THUMBNAILS };
1787395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            for (String table : tables) {
1788395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                db.execSQL("UPDATE " + table + " SET " + "_data='" + externalStorage
1789395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                        + "'||SUBSTR(_data,17) WHERE _data LIKE '/storage/sdcard0/%';");
1790395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            }
1791395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        }
17928bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey        if (fromVersion < 511) {
17938bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey            // we update _data in version 510, we need to update the bucket_id as well
17948bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey            updateBucketNames(db);
17958bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey        }
1796395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1797e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // JB 4.2 went out with database version 511, starting next release with 600
1798e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1799e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        if (fromVersion < 600) {
1800e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // modify _data column to be unique and collate nocase. Because this drops the original
1801e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // table and replaces it with a new one by the same name, we need to also recreate all
1802e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // indices and triggers that refer to the files table.
1803e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // Views don't need to be recreated.
1804e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1805e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE TABLE files2 (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1806e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_data TEXT UNIQUE" +
1807e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    // the internal filesystem is case-sensitive
1808e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    (internal ? "," : " COLLATE NOCASE,") +
1809e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_size INTEGER,format INTEGER,parent INTEGER,date_added INTEGER," +
1810e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "date_modified INTEGER,mime_type TEXT,title TEXT,description TEXT," +
1811e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_display_name TEXT,picasa_id TEXT,orientation INTEGER,latitude DOUBLE," +
1812e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER,bucket_id TEXT," +
1813e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "bucket_display_name TEXT,isprivate INTEGER,title_key TEXT,artist_id INTEGER," +
1814e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "album_id INTEGER,composer TEXT,track INTEGER,year INTEGER CHECK(year!=0)," +
1815e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "is_ringtone INTEGER,is_music INTEGER,is_alarm INTEGER," +
1816e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "is_notification INTEGER,is_podcast INTEGER,album_artist TEXT," +
1817e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "duration INTEGER,bookmark INTEGER,artist TEXT,album TEXT,resolution TEXT," +
1818e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "tags TEXT,category TEXT,language TEXT,mini_thumb_data TEXT,name TEXT," +
1819e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "media_type INTEGER,old_id INTEGER,storage_id INTEGER,is_drm INTEGER," +
1820e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "width INTEGER, height INTEGER);");
1821e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1822e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // copy data from old table, squashing entries with duplicate _data
1823e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("INSERT OR REPLACE INTO files2 SELECT * FROM files;");
1824e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("DROP TABLE files;");
1825e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("ALTER TABLE files2 RENAME TO files;");
1826e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1827e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // recreate indices and triggers
1828e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1829e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1830e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX bucket_index on files(bucket_id,media_type," +
1831e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "datetaken, _id);");
1832e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX bucket_name on files(bucket_id,media_type," +
1833e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "bucket_display_name);");
1834e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX format_index ON files(format);");
1835e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1836e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX parent_index ON files(parent);");
1837e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX path_index ON files(_data);");
1838e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1839e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX title_idx ON files(title);");
1840e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1841e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            if (!internal) {
1842e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                db.execSQL("CREATE TRIGGER audio_playlists_cleanup DELETE ON files" +
1843e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " WHEN old.media_type=4" +
1844e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " BEGIN DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1845e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        "SELECT _DELETE_FILE(old._data);END;");
1846e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                db.execSQL("CREATE TRIGGER files_cleanup DELETE ON files" +
1847e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " BEGIN SELECT _OBJECT_REMOVED(old._id);END;");
1848e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            }
1849e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        }
1850e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1851f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen        if (fromVersion < 601) {
1852f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            // remove primary key constraint because column time is not necessarily unique
1853f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS log_tmp (time DATETIME, message TEXT);");
1854f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("DELETE FROM log_tmp;");
1855f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("INSERT INTO log_tmp SELECT time, message FROM log order by rowid;");
1856f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("DROP TABLE log;");
1857f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("ALTER TABLE log_tmp RENAME TO log;");
1858f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen        }
1859f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen
18602a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen        if (fromVersion < 700) {
18612a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // fix datetaken fields that were added with an incorrect timestamp
18622a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // datetaken needs to be in milliseconds, so should generally be a few orders of
18632a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // magnitude larger than date_modified. If it's within the same order of magnitude, it
18642a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // is probably wrong.
18652a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // (this could do the wrong thing if your picture was actually taken before ~3/21/1970)
18662a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            db.execSQL("UPDATE files set datetaken=date_modified*1000"
18672a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen                    + " WHERE date_modified IS NOT NULL"
18682a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen                    + " AND datetaken IS NOT NULL"
18692a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen                    + " AND datetaken<date_modified*5;");
18702a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen        }
18712a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen
1872407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood       if (fromVersion < 800) {
1873407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood            // Delete albums and artists, then clear the modification time on songs, which
1874407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood            // will cause the media scanner to rescan everything, rebuilding the artist and
1875407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood            // album tables along the way, while preserving playlists.
1876407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood            // We need this rescan because ICU also changed, and now generates different
1877407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood            // collation keys
1878407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood            db.execSQL("DELETE from albums");
1879407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood            db.execSQL("DELETE from artists");
1880407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood            db.execSQL("UPDATE files SET date_modified=0;");
1881407c6ddc5ac8eced85ce82fae59a75ac27a989a5Mike Lockwood        }
18822a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen
1883acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sanityCheck(db, fromVersion);
1884988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000;
1885988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion
1886988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                + " in " + elapsedSeconds + " seconds");
1887988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    }
1888988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1889988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    /**
1890988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen     * Write a persistent diagnostic message to the log table.
1891988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen     */
1892988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    static void logToDb(SQLiteDatabase db, String message) {
1893934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen        db.execSQL("INSERT OR REPLACE" +
1894934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen                " INTO log (time,message) VALUES (strftime('%Y-%m-%d %H:%M:%f','now'),?);",
1895988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                new String[] { message });
1896988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        // delete all but the last 500 rows
1897988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        db.execSQL("DELETE FROM log WHERE rowid IN" +
1898f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen                " (SELECT rowid FROM log ORDER BY rowid DESC LIMIT 500,-1);");
18991d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    }
19001d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen
19011d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    /**
1902216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * Perform a simple sanity check on the database. Currently this tests
1903216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * whether all the _data entries in audio_meta are unique
1904216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     */
1905216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen    private static void sanityCheck(SQLiteDatabase db, int fromVersion) {
1906a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        Cursor c1 = null;
1907a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        Cursor c2 = null;
1908a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        try {
1909a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            c1 = db.query("audio_meta", new String[] {"count(*)"},
1910a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    null, null, null, null, null);
1911a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            c2 = db.query("audio_meta", new String[] {"count(distinct _data)"},
1912a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    null, null, null, null, null);
1913a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            c1.moveToFirst();
1914a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            c2.moveToFirst();
1915a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            int num1 = c1.getInt(0);
1916a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            int num2 = c2.getInt(0);
1917a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            if (num1 != num2) {
1918a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                Log.e(TAG, "audio_meta._data column is not unique while upgrading" +
1919a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        " from schema " +fromVersion + " : " + num1 +"/" + num2);
1920a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                // Delete all audio_meta rows so they will be rebuilt by the media scanner
1921a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                db.execSQL("DELETE FROM audio_meta;");
1922a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            }
1923a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        } finally {
1924a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c1);
1925a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c2);
1926216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        }
1927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
1930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
1931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
1932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
1933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
1934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
1935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
193695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1938677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen     * Update the bucket_id and bucket_display_name columns for images and videos
1939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1942677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen    private static void updateBucketNames(SQLiteDatabase db) {
1943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
1944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
1947677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            // update only images and videos
1948677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            Cursor cursor = db.query("files", columns, "media_type=1 OR media_type=3",
1949677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen                    null, null, null, null);
1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
19539491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                String [] rowId = new String[1];
1954988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                ContentValues values = new ContentValues();
1955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
1957988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    rowId[0] = cursor.getString(idColumnIndex);
1958d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    if (data != null) {
1959988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        values.clear();
1960d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        computeBucketValues(data, values);
1961677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen                        db.update("files", values, "_id=?", rowId);
1962d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    } else {
1963d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        Log.w(TAG, "null data at id " + rowId);
1964d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    }
1965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1967a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(cursor);
1968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
1977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
1978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
1982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
1983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
1986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
1990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
1991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
1992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
1994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
1995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
1996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
1997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
1998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
1999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
2000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
2003a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(cursor);
2004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
2006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
2008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2010988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
2011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
2013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
2014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
2017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
2018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
2019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
2020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
2023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
2024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
2025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
2026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
2027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
2029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
2030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
2031d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood        values.put(ImageColumns.BUCKET_ID, path.hashCode());
2032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
2033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
2037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
2038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
2041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
2042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
2043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
2044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
2045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
2047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2049b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
2050498b62c2912302a23532c73a028a7684c5df33caRay Chen     * Copy taken time from date_modified if we lost the original value (e.g. after factory reset)
2051498b62c2912302a23532c73a028a7684c5df33caRay Chen     * This works for both video and image tables.
2052b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     *
2053b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     * @param values the content values, where taken time is updated.
2054b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     */
2055b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    private static void computeTakenTime(ContentValues values) {
2056b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        if (! values.containsKey(Images.Media.DATE_TAKEN)) {
2057b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // This only happens when MediaScanner finds an image file that doesn't have any useful
2058b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // reference to get this value. (e.g. GPSTimeStamp)
2059498b62c2912302a23532c73a028a7684c5df33caRay Chen            Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED);
2060b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            if (lastModified != null) {
2061b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                values.put(Images.Media.DATE_TAKEN, lastModified * 1000);
2062b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            }
2063b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        }
2064b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    }
2065b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen
2066b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    /**
2067b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
2068b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
2069b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
2070b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
2071b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
2072b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
2073b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
2074b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
2075b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
2076a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        try {
2077a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            if (c != null && c.moveToFirst()) {
2078a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                long id = c.getLong(0);
2079a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                String path = c.getString(1);
2080a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                long magic = c.getLong(2);
2081a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson
2082a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                MediaThumbRequest req = requestMediaThumbnail(path, origUri,
2083a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        MediaThumbRequest.PRIORITY_HIGH, magic);
2084a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                if (req != null) {
2085a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    synchronized (req) {
2086a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        try {
2087a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                            while (req.mState == MediaThumbRequest.State.WAIT) {
2088a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                req.wait();
2089a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                            }
2090a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        } catch (InterruptedException e) {
2091a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                            Log.w(TAG, e);
2092a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        }
2093a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        if (req.mState == MediaThumbRequest.State.DONE) {
2094a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                            result = true;
2095a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        }
209620434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
2097b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2098b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
2099a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        } finally {
2100a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c);
2101b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2102b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
2103b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
2104b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2105e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid,
2106e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo) {
2107e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllOrigId = (id == -1);
2108e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllGroupId = (gid == -1);
2109e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        return (req.mCallingPid == pid) &&
2110e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllGroupId || req.mGroupId == gid) &&
2111e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllOrigId || req.mOrigId == id) &&
2112e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (req.mIsVideo == isVideo);
2113e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    }
2114e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
2115b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
2116b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
2117b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
2118b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
2119b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
2120b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
2121b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
2122b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
2123b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
2124b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2125b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
2126b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
2127b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
2128b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
2129b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
2130b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2131b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2132b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
213320434e032e498b716f87cce2f23dd646819218bfRay Chen        boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel"));
2134e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        Uri origUri = uri.buildUpon().encodedPath(
2135e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                uri.getPath().replaceFirst("thumbnails", "media"))
2136e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                .appendPath(origId).build();
2137b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2138b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
213920434e032e498b716f87cce2f23dd646819218bfRay Chen            Log.w(TAG, "original media doesn't exist or it's canceled.");
2140b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
214120434e032e498b716f87cce2f23dd646819218bfRay Chen        } else if (cancelRequest) {
2142e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            String groupId = uri.getQueryParameter("group_id");
2143e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo = "video".equals(uri.getPathSegments().get(1));
214420434e032e498b716f87cce2f23dd646819218bfRay Chen            int pid = Binder.getCallingPid();
214520434e032e498b716f87cce2f23dd646819218bfRay Chen            long id = -1;
2146e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            long gid = -1;
2147e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
214820434e032e498b716f87cce2f23dd646819218bfRay Chen            try {
214920434e032e498b716f87cce2f23dd646819218bfRay Chen                id = Long.parseLong(origId);
2150e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                gid = Long.parseLong(groupId);
215120434e032e498b716f87cce2f23dd646819218bfRay Chen            } catch (NumberFormatException ex) {
215220434e032e498b716f87cce2f23dd646819218bfRay Chen                // invalid cancel request
215320434e032e498b716f87cce2f23dd646819218bfRay Chen                return false;
215420434e032e498b716f87cce2f23dd646819218bfRay Chen            }
2155e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
215620434e032e498b716f87cce2f23dd646819218bfRay Chen            synchronized (mMediaThumbQueue) {
2157e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                if (mCurrentThumbRequest != null &&
2158e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                        matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) {
215920434e032e498b716f87cce2f23dd646819218bfRay Chen                    synchronized (mCurrentThumbRequest) {
216020434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL;
216120434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.notifyAll();
216220434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
216320434e032e498b716f87cce2f23dd646819218bfRay Chen                }
216420434e032e498b716f87cce2f23dd646819218bfRay Chen                for (MediaThumbRequest mtq : mMediaThumbQueue) {
2165e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                    if (matchThumbRequest(mtq, pid, id, gid, isVideo)) {
216620434e032e498b716f87cce2f23dd646819218bfRay Chen                        synchronized (mtq) {
216720434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.mState = MediaThumbRequest.State.CANCEL;
216820434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.notifyAll();
216920434e032e498b716f87cce2f23dd646819218bfRay Chen                        }
217020434e032e498b716f87cce2f23dd646819218bfRay Chen
217120434e032e498b716f87cce2f23dd646819218bfRay Chen                        mMediaThumbQueue.remove(mtq);
217220434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
217320434e032e498b716f87cce2f23dd646819218bfRay Chen                }
217420434e032e498b716f87cce2f23dd646819218bfRay Chen            }
2175b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2176b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2177b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
2178b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
2179b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2180b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
2181b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
218201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
218301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    @Override
218401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    public Uri canonicalize(Uri uri) {
218501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        int match = URI_MATCHER.match(uri);
218601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
218701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        // only support canonicalizing specific audio Uris
218801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (match != AUDIO_MEDIA_ID) {
218901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return null;
219001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
219101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        Cursor c = query(uri, null, null, null, null);
2192a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        String title = null;
2193a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        Uri.Builder builder = null;
2194a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson
2195a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        try {
2196a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            if (c == null || c.getCount() != 1 || !c.moveToNext()) {
2197a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                return null;
2198a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            }
219901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
2200a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            // Construct a canonical Uri by tacking on some query parameters
2201a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            builder = uri.buildUpon();
2202a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            builder.appendQueryParameter(CANONICAL, "1");
2203a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            title = c.getString(c.getColumnIndex(MediaStore.Audio.Media.TITLE));
2204a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        } finally {
2205a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c);
2206a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        }
220701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (TextUtils.isEmpty(title)) {
220801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return null;
220901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
221001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        builder.appendQueryParameter(MediaStore.Audio.Media.TITLE, title);
221101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        Uri newUri = builder.build();
221201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        return newUri;
221301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    }
221401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
221501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    @Override
221601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    public Uri uncanonicalize(Uri uri) {
221701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (uri != null && "1".equals(uri.getQueryParameter(CANONICAL))) {
221801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            int match = URI_MATCHER.match(uri);
221901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            if (match != AUDIO_MEDIA_ID) {
222001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                // this type of canonical Uri is not supported
222101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                return null;
222201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            }
222301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            String titleFromUri = uri.getQueryParameter(MediaStore.Audio.Media.TITLE);
222401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            if (titleFromUri == null) {
222501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                // the required parameter is missing
222601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                return null;
222701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            }
222801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            // clear the query parameters, we don't need them anymore
222901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            uri = uri.buildUpon().clearQuery().build();
223001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
223101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            Cursor c = query(uri, null, null, null, null);
2232a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            try {
2233a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                int titleIdx = c.getColumnIndex(MediaStore.Audio.Media.TITLE);
2234a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                if (c != null && c.getCount() == 1 && c.moveToNext() &&
2235a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        titleFromUri.equals(c.getString(titleIdx))) {
2236a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    // the result matched perfectly
2237a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    return uri;
2238a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                }
223901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
2240a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(c);
2241a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                // do a lookup by title
2242a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                Uri newUri = MediaStore.Audio.Media.getContentUri(uri.getPathSegments().get(0));
224301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
2244a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                c = query(newUri, null, MediaStore.Audio.Media.TITLE + "=?",
2245a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        new String[] {titleFromUri}, null);
2246a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                if (c == null) {
2247a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    return null;
2248a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                }
2249a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                if (!c.moveToNext()) {
2250a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    return null;
2251a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                }
2252a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                // get the first matching entry and return a Uri for it
2253a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                long id = c.getLong(c.getColumnIndex(MediaStore.Audio.Media._ID));
2254a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                return ContentUris.withAppendedId(newUri, id);
2255a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            } finally {
2256a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(c);
225701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            }
225801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
225901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        return uri;
226001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    }
226101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
226201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    private Uri safeUncanonicalize(Uri uri) {
226301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        Uri newUri = uncanonicalize(uri);
226401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (newUri != null) {
226501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return newUri;
226601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
226701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        return uri;
226801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    }
226901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
2270b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
2271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
2273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
227401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
227501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        uri = safeUncanonicalize(uri);
227601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
2277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
2278baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        List<String> prependArgs = new ArrayList<String>();
2279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
228001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
2281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
2282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
2283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
2284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
2285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
22870027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
22880027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                c.addRow(new String[] {mMediaScannerVolume});
22890027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                return c;
2290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
22930027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // Used temporarily (until we have unique media IDs) to get an identifier
22940027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // for the current sd card, so that the music app doesn't have to use the
22950027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // non-public getFatVolumeId method
22960027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        if (table == FS_ID) {
22970027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"fsid"});
22980027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            c.addRow(new Integer[] {mVolumeId});
22990027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            return c;
23000027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        }
23010027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
2302704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        if (table == VERSION) {
2303704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"version"});
230490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            c.addRow(new Integer[] {getDatabaseVersion(getContext())});
2305704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            return c;
2306704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        }
2307704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen
2308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
230910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
231010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
2312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
231310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
231410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getReadableDatabase();
23155fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) return null;
2316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
23174574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
2318c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String filter = uri.getQueryParameter("filter");
2319c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String [] keywords = null;
2320c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        if (filter != null) {
2321c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            filter = Uri.decode(filter).trim();
2322c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            if (!TextUtils.isEmpty(filter)) {
2323c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                String [] searchWords = filter.split(" ");
2324c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                keywords = new String[searchWords.length];
2325c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; i < searchWords.length; i++) {
2326c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    String key = MediaStore.Audio.keyFor(searchWords[i]);
2327c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("\\", "\\\\");
2328c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("%", "\\%");
2329c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("_", "\\_");
2330c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    keywords[i] = key;
2331c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2332c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            }
2333c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        }
2334db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (uri.getQueryParameter("distinct") != null) {
2335db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            qb.setDistinct(true);
2336db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
2337c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen
2338b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
2339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
2341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2357baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2358baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2362b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2363b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
2364b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
2365b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2366b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2370d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2371ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.equalsIgnoreCase("is_music=1")
2372ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                          || selection.equalsIgnoreCase("is_podcast=1") )
2373c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2374c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2375ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting songs");
2376ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2377ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2378ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio");
2379c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2380c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2381c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2382c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2383c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2384c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2385baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'");
2386baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2387c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2388ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
2393baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2394baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
2400baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_genres_map WHERE audio_id=?)");
2401baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2406baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2407baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
2413baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_playlists_map WHERE audio_id=?)");
2414baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2419baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2420baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2429baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2430baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2433bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen            case AUDIO_GENRES_ALL_MEMBERS:
2434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
243578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                {
243678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // if simpleQuery is true, we can do a simpler query on just audio_genres_map
243778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // we can do this if we have no keywords and our projection includes just columns
243878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // from audio_genres_map
243978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    boolean simpleQuery = (keywords == null && projectionIn != null
244078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            && (selection == null || selection.equalsIgnoreCase("genre_id=?")));
244178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (projectionIn != null) {
244278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; i < projectionIn.length; i++) {
244378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            String p = projectionIn[i];
244478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (p.equals("_id")) {
244578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // note, this is different from playlist below, because
244678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // "_id" used to (wrongly) be the audio id in this query, not
244778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // the row id of the entry in the map, and we preserve this
244878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // behavior for backwards compatibility
244978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
245078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
245178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (simpleQuery && !(p.equals("audio_id") ||
245278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    p.equals("genre_id"))) {
245378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
245478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
245578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
245678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
245778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (simpleQuery) {
245878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid");
2459bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2460baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere("genre_id=?");
2461baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2462bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
246378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    } else {
246478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid, audio");
2465bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        qb.appendWhere("audio._id = audio_id");
2466bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2467baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere(" AND genre_id=?");
2468baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2469bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
247078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; keywords != null && i < keywords.length; i++) {
247178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(" AND ");
247278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
247378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.ALBUM_KEY +
247478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.TITLE_KEY +
2475baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                    " LIKE ? ESCAPE '\\'");
2476baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add("%" + keywords[i] + "%");
247778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
247878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
247978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                }
2480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2488baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2489baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2492b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2494e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // if simpleQuery is true, we can do a simpler query on just audio_playlists_map
2495e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // we can do this if we have no keywords and our projection includes just columns
2496e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // from audio_playlists_map
24974382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                boolean simpleQuery = (keywords == null && projectionIn != null
24984382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                        && (selection == null || selection.equalsIgnoreCase("playlist_id=?")));
249997e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
250097e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
2501e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        String p = projectionIn[i];
2502e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (simpleQuery && !(p.equals("audio_id") ||
2503e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                p.equals("playlist_id") || p.equals("play_order"))) {
2504e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                            simpleQuery = false;
2505e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        }
2506e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (p.equals("_id")) {
250797e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
250897e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
2509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2511e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                if (simpleQuery) {
2512e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map");
2513baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("playlist_id=?");
2514baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2515e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                } else {
2516e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map, audio");
2517baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("audio._id = audio_id AND playlist_id=?");
2518baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2519e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2520e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(" AND ");
2521e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2522e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2523e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.TITLE_KEY +
2524baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2525baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2526e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    }
2527c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2528b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                if (table == AUDIO_PLAYLISTS_ID_MEMBERS_ID) {
2529baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere(" AND audio_playlists_map._id=?");
2530baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(5));
2531b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                }
2532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2539baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2540baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2543b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
2544b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2545b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
2546b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
2547b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2548b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2549b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2550b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
2552d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2553ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2554c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2555c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2556ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting artists");
2557ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2558ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct artist_id)";
2559ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2560ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2561ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("artist_info");
2562c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2563c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2564c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2565c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2566c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2567baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2568baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2569c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2570ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
2574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
2575baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2576baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
2580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
2581acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
2582acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        " audio.album_id=album_art.album_id");
2583acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
2584baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "artists_albums_map WHERE artist_id=?)");
2585baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(aid);
2586c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; keywords != null && i < keywords.length; i++) {
2587c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(" AND ");
2588c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2589c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            "||" + MediaStore.Audio.Media.ALBUM_KEY +
2590baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            " LIKE ? ESCAPE '\\'");
2591baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add("%" + keywords[i] + "%");
2592c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2593acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                groupBy = "audio.album_id";
2594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
2595acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
2596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
2597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
2598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
2601d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2602ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2603c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2604c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2605ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting albums");
2606ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2607ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct album_id)";
2608ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2609ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2610ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("album_info");
2611c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2612c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2613c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2614c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2615c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2616c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2617baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2618baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2619c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2620ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
2624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
2625baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2626baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
2631baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("album_id=?");
2632baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2635a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
2636a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
2637a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
2638a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
2639a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
2640baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection,
2641baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        combine(prependArgs, selectionArgs), sort, table, limit);
2642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
264316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
2644e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
2645baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2646baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(2));
2647b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                // fall through
264816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
2649e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
265016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                qb.setTables("files");
2651b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                break;
2652b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2653e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            case MTP_OBJECT_REFERENCES:
2654e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int handle = Integer.parseInt(uri.getPathSegments().get(2));
265510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                return getObjectReferences(helper, db, handle);
2656e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
2658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
2659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2661baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection,
2662baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        //        combine(prependArgs, selectionArgs), groupBy, null, sort, limit));
2663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
2664baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
2665b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
2667ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            String nonotify = uri.getQueryParameter("nonotify");
2668ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            if (nonotify == null || !nonotify.equals("1")) {
2669ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen                c.setNotificationUri(getContext().getContentResolver(), uri);
2670ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            }
2671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2672b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
2674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2676baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    private String[] combine(List<String> prepend, String[] userArgs) {
2677baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int presize = prepend.size();
2678baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        if (presize == 0) {
2679baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            return userArgs;
2680baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2681baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2682baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int usersize = (userArgs != null) ? userArgs.length : 0;
2683baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        String [] combined = new String[presize + usersize];
2684baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < presize; i++) {
2685baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[i] = prepend.get(i);
2686baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2687baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < usersize; i++) {
2688baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[presize + i] = userArgs[i];
2689baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2690baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        return combined;
2691baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    }
2692baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
2694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
26954574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
26964574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
2697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
269818c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen        String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment();
2699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
2700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
2702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
2703a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
2704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
2705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
2706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
2707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
27083001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            String key = MediaStore.Audio.keyFor(searchWords[i]);
27093001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("\\", "\\\\");
27103001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("%", "\\%");
27113001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("_", "\\_");
2712a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
2713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
27143001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                        searchWords[i].equals("the")) ? "%" : "%" + key + "%";
2715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2717a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
2718a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
2719a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
27203001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where = "match LIKE ? ESCAPE '\\'";
2721a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
27223001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where += " AND match LIKE ? ESCAPE '\\'";
2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2726a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
2727a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
2728a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
2729a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
2730a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
2731a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
2732a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
2733a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
2734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
27354574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
2736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
2740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
2742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2746c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case FILES_ID:
274726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                Cursor c = null;
274826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                try {
274926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    c = query(url, MIME_TYPE_PROJECTION, null, null, null);
275026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null && c.getCount() == 1) {
275126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.moveToFirst();
275226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        String mimeType = c.getString(1);
275326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.deactivate();
275426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        return mimeType;
275526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
275626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                } finally {
2757a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    IoUtils.closeQuietly(c);
2758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
2763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
2764804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen            case AUDIO_ALBUMART_ID:
2765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
2767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
2770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
2772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
2776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
2779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
2782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
2785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
2788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2789804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen        throw new IllegalStateException("Unknown URL : " + url);
2790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
279450c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen     * present a new filename is generated. The file itself is not created.
2795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
2797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
2798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
2800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
2801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
2802801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        String file = initialValues.getAsString(MediaStore.MediaColumns.DATA);
2803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
2804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
2805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
2806801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            values.put(MediaStore.MediaColumns.DATA, file);
2807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
2809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
281192013781ab1573eee3d5d75682f320fe3a6076f2Marco Nelissen        // we used to create the file here, but now defer this until openFile() is called
2812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
2813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2815d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectAdded(long objectHandle) {
281634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
281734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
281834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
281934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectAdded((int)objectHandle);
282034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
282134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectAdded", e);
282234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
282334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2824d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2825d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2826d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2827d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2828d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectRemoved(long objectHandle) {
282934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
283034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
283134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
283234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectRemoved((int)objectHandle);
283334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
283434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectRemoved", e);
283534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
283634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2837d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2838d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2839d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2840d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
2843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
2845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
2846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
284710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
284810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
285210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
28535fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) {
28545fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            throw new IllegalStateException("Couldn't open database for " + uri);
28555fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        }
2856ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2857ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
2858ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            return playlistBulkInsert(db, uri, values);
2859e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } else if (match == MTP_OBJECT_REFERENCES) {
2860e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            int handle = Integer.parseInt(uri.getPathSegments().get(2));
286110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            return setObjectReferences(helper, db, handle, values);
2862ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2863ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
28642dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
28662dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
2867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
2868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
2870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
2871801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                if (values[i] != null) {
28722dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                    insertInternal(uri, match, values[i], notifyRowIds);
2873801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                }
2874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
2876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
2877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
2879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
28802dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
28812dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        // Notify MTP (outside of successful transaction)
28822dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        notifyMtp(notifyRowIds);
28832dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
2885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
2886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2889801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood    public Uri insert(Uri uri, ContentValues initialValues) {
28905d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        int match = URI_MATCHER.match(uri);
28912dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
28922dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
28932dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
28942dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        notifyMtp(notifyRowIds);
28952dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
28965d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // do not signal notification for MTP objects.
28975d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // we will signal instead after file transfer is successful.
2898e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        if (newUri != null && match != MTP_OBJECTS) {
2899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
2900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
2902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
29042dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    private void notifyMtp(ArrayList<Long> rowIds) {
29052dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        int size = rowIds.size();
29062dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        for (int i = 0; i < size; i++) {
29072dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke            sendObjectAdded(rowIds.get(i).longValue());
29082dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        }
29092dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    }
29102dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2911ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) {
2912ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        DatabaseUtils.InsertHelper helper =
2913ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            new DatabaseUtils.InsertHelper(db, "audio_playlists_map");
29148b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
29158b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID);
29168b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
29178b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        long playlistId = Long.parseLong(uri.getPathSegments().get(3));
2918ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2919ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        db.beginTransaction();
2920ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        int numInserted = 0;
2921ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        try {
2922ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            int len = values.length;
2923ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            for (int i = 0; i < len; i++) {
29248b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.prepareForInsert();
29258b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // getting the raw Object and converting it long ourselves saves
29268b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // an allocation (the alternative is ContentValues.getAsLong, which
29278b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // returns a Long object)
29288b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                long audioid = ((Number) values[i].get(
29298b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue();
29308b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(audioidcolidx, audioid);
29318b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playlistididx, playlistId);
29328b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // convert to int ourselves to save an allocation.
29338b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                int playorder = ((Number) values[i].get(
29348b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue();
29358b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playorderidx, playorder);
29368b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.execute();
2937ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            }
2938ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            numInserted = len;
2939ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.setTransactionSuccessful();
2940ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        } finally {
2941ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.endTransaction();
2942ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            helper.close();
2943ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2944ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        getContext().getContentResolver().notifyChange(uri, null);
2945ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        return numInserted;
2946ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    }
2947ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
294810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertDirectory(DatabaseHelper helper, SQLiteDatabase db, String path) {
294910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (LOCAL_LOGV) Log.v(TAG, "inserting directory " + path);
2950ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        ContentValues values = new ContentValues();
2951ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION);
2952ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.DATA, path);
295310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        values.put(FileColumns.PARENT, getParent(helper, db, path));
29549be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        values.put(FileColumns.STORAGE_ID, getStorageId(path));
2955f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        File file = new File(path);
2956f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        if (file.exists()) {
2957f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
2958f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        }
295910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumInserts++;
2960ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2961ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        sendObjectAdded(rowId);
2962ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        return rowId;
2963ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
2964ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
296510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getParent(DatabaseHelper helper, SQLiteDatabase db, String path) {
2966b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        int lastSlash = path.lastIndexOf('/');
2967b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (lastSlash > 0) {
2968b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String parentPath = path.substring(0, lastSlash);
29699be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            for (int i = 0; i < mExternalStoragePaths.length; i++) {
29709be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (parentPath.equals(mExternalStoragePaths[i])) {
29719be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return 0;
29729be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
2973b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
29747f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Long cid = mDirectoryCache.get(parentPath);
29757f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            if (cid != null) {
29767f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath);
29777f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return cid;
29787f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            }
29797f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
2980e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            String selection = MediaStore.MediaColumns.DATA + "=?";
2981b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String [] selargs = { parentPath };
298210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
29837f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
2984b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            try {
29857f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                long id;
2986b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c == null || c.getCount() == 0) {
2987b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    // parent isn't in the database - so add it
298810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    id = insertDirectory(helper, db, parentPath);
29897f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath);
2990b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                } else {
29915461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    if (c.getCount() > 1) {
29925461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        Log.e(TAG, "more than one match for " + parentPath);
29935461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    }
2994b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    c.moveToFirst();
29957f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    id = c.getLong(0);
29967f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath);
2997b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                }
29987f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.put(parentPath, id);
29997f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return id;
3000b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            } finally {
3001a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(c);
3002b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
3003b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        } else {
3004b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            return 0;
3005b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        }
3006b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    }
3007b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
30089be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private int getStorageId(String path) {
30099be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        for (int i = 0; i < mExternalStoragePaths.length; i++) {
30109be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String test = mExternalStoragePaths[i];
30119be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (path.startsWith(test)) {
30129be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int length = test.length();
30139be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (path.length() == length || path.charAt(length) == '/') {
30149be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return MtpStorage.getStorageId(i);
30159be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
30169be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
30179be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
30189be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        // default to primary storage
30199be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        return MtpStorage.getStorageId(0);
30209be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    }
30219be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
302210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, int mediaType,
30232dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                            boolean notify, ArrayList<Long> notifyRowIds) {
302410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
3025afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        ContentValues values = null;
3026afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3027afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        switch (mediaType) {
3028afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_IMAGE: {
30294d214a393b14df4a544a07bbaadcab4dbb67379eMarco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".jpg", "Pictures");
3030afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3031afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
3032afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String data = values.getAsString(MediaColumns.DATA);
3033afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
3034afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    computeDisplayName(data, values);
3035afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3036afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
3037afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
3038afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
3039afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3040afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_AUDIO: {
3041afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // SQLite Views are read-only, so we need to deconstruct this
3042afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // insert and do inserts into the underlying tables.
3043afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // If doing this here turns out to be a performance bottleneck,
3044afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // consider moving this to native code and using triggers on
3045afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the view.
3046afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values = new ContentValues(initialValues);
3047afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
30482658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
30492658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
30502658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                values.remove(MediaStore.Audio.Media.COMPILATION);
30512658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
3052afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Insert the artist into the artist table and remove it from
3053afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the input values
3054afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                Object so = values.get("artist");
3055afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String s = (so == null ? "" : so.toString());
3056afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("artist");
3057afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long artistRowId;
305810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> artistCache = helper.mArtistCache;
3059801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String path = values.getAsString(MediaStore.MediaColumns.DATA);
3060afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(artistCache) {
3061afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = artistCache.get(s);
3062afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
306310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        artistRowId = getKeyIdForName(helper, db,
306410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "artists", "artist_key", "artist",
3065afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, s, path, 0, null, artistCache, uri);
3066afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
3067afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        artistRowId = temp.longValue();
3068afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
3069afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3070afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String artist = s;
3071afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3072afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Do the same for the album field
3073afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.get("album");
3074afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
3075afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("album");
3076afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long albumRowId;
307710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> albumCache = helper.mAlbumCache;
3078afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(albumCache) {
30792658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    int albumhash = 0;
30802658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    if (albumartist != null) {
30812658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = albumartist.hashCode();
30822658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else if (compilation != null && compilation.equals("1")) {
30832658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        // nothing to do, hash already set
30842658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else {
30852658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
30862658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    }
3087afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    String cacheName = s + albumhash;
3088afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = albumCache.get(cacheName);
3089afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
309010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        albumRowId = getKeyIdForName(helper, db,
309110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "albums", "album_key", "album",
3092afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, cacheName, path, albumhash, artist, albumCache, uri);
3093afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
3094afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        albumRowId = temp;
3095afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
3096afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
30975d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
3098afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("artist_id", Integer.toString((int)artistRowId));
3099afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("album_id", Integer.toString((int)albumRowId));
3100afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.getAsString("title");
3101afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
3102afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title_key", MediaStore.Audio.keyFor(s));
3103afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // do a final trim of the title, in case it started with the special
3104afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // "sort first" character (ascii \001)
3105afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("title");
3106afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title", s.trim());
3107b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3108801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                computeDisplayName(values.getAsString(MediaStore.MediaColumns.DATA), values);
3109afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
3110afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
3111afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3112afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_VIDEO: {
311310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".3gp", "video");
3114801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String data = values.getAsString(MediaStore.MediaColumns.DATA);
3115afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeDisplayName(data, values);
3116afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
3117afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
3118afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
3119afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
3120afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3121afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (values == null) {
3122afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(initialValues);
3123afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
3124fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // compute bucket_id and bucket_display_name for all files
3125afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String path = values.getAsString(MediaStore.MediaColumns.DATA);
3126fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        if (path != null) {
3127fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            computeBucketValues(path, values);
3128fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        }
3129fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
3130afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3131afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        long rowId = 0;
3132afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer i = values.getAsInteger(
3133afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
3134afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (i != null) {
3135afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = i.intValue();
3136afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(values);
3137afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
3138afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
3139afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3140afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String title = values.getAsString(MediaStore.MediaColumns.TITLE);
31413572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen        if (title == null && path != null) {
3142c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            title = MediaFile.getFileTitle(path);
3143c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3144c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        values.put(FileColumns.TITLE, title);
3145c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
3146afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE);
3147afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer formatObject = values.getAsInteger(FileColumns.FORMAT);
3148c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        int format = (formatObject == null ? 0 : formatObject.intValue());
3149c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format == 0) {
315063b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang            if (TextUtils.isEmpty(path)) {
3151c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                // special case device created playlists
3152afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
3153c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST);
3154c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    // create a file path for the benefit of MTP
31559be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    path = mExternalStoragePaths[0]
3156afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            + "/Playlists/" + values.getAsString(Audio.Playlists.NAME);
3157c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(MediaStore.MediaColumns.DATA, path);
315810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    values.put(FileColumns.PARENT, getParent(helper, db, path));
3159c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                } else {
316063b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang                    Log.e(TAG, "path is empty in insertFile()");
3161c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                }
3162c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            } else {
3163c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                format = MediaFile.getFormatCode(path, mimeType);
3164c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3165c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3166c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format != 0) {
3167c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.FORMAT, format);
3168c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            if (mimeType == null) {
3169c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                mimeType = MediaFile.getMimeTypeForFormatCode(format);
3170c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3171c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3172c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
3173801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        if (mimeType == null && path != null) {
3174c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            mimeType = MediaFile.getMimeTypeForFile(path);
3175c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3176c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (mimeType != null) {
3177c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.MIME_TYPE, mimeType);
3178afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3179f1f6a9e343033de33fc748f659b9221f8d5b06e1Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_NONE && !MediaScanner.isNoMediaPath(path)) {
3180afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int fileType = MediaFile.getFileTypeForMimeType(mimeType);
3181afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (MediaFile.isAudioFileType(fileType)) {
3182afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_AUDIO;
3183afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isVideoFileType(fileType)) {
3184afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_VIDEO;
3185afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isImageFileType(fileType)) {
3186afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_IMAGE;
3187afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isPlayListFileType(fileType)) {
3188afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
3189afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3190afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
3191c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3192afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        values.put(FileColumns.MEDIA_TYPE, mediaType);
3193c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
3194afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (rowId == 0) {
3195afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
31963572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen                String name = values.getAsString(Audio.Playlists.NAME);
3197282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                if (name == null && path == null) {
3198282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                    // MediaScanner will compute the name from the path if we have one
3199afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
3200282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                            "no name was provided when inserting abstract playlist");
3201afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3202afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            } else {
3203a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu                if (path == null) {
3204afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // path might be null for playlists created on the device
3205afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // or transfered via MTP
3206afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
3207afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "no path was provided when inserting new file");
3208afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3209e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3210b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3211f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            // make sure modification date and size are set
3212f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            if (path != null) {
3213f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                File file = new File(path);
3214f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                if (file.exists()) {
3215f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                    values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
321634d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                    if (!values.containsKey(FileColumns.SIZE)) {
321734d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                        values.put(FileColumns.SIZE, file.length());
321834d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                    }
321920368a558593c685623bfff6df1f4545d53f8291Jia Su                    // make sure date taken time is set
322020368a558593c685623bfff6df1f4545d53f8291Jia Su                    if (mediaType == FileColumns.MEDIA_TYPE_IMAGE
322120368a558593c685623bfff6df1f4545d53f8291Jia Su                            || mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
322220368a558593c685623bfff6df1f4545d53f8291Jia Su                        computeTakenTime(values);
322320368a558593c685623bfff6df1f4545d53f8291Jia Su                    }
3224e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
32255d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
3226b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3227afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            Long parent = values.getAsLong(FileColumns.PARENT);
32285d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (parent == null) {
3229e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path != null) {
323010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    long parentId = getParent(helper, db, path);
323116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.PARENT, parentId);
3232e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
32339be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
32349be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            Integer storage = values.getAsInteger(FileColumns.STORAGE_ID);
32359be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (storage == null) {
32369be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int storageId = getStorageId(path);
32379be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                values.put(FileColumns.STORAGE_ID, storageId);
32385d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
3239b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
324010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumInserts++;
3241afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
3242afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId);
3243afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
324410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (rowId != -1 && notify) {
32452dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                notifyRowIds.add(rowId);
3246afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
32475d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } else {
324810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates++;
324916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.update("files", values, FileColumns._ID + "=?",
3250afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    new String[] { Long.toString(rowId) });
32515d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        }
3252d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen        if (format == MtpConstants.FORMAT_ASSOCIATION) {
3253d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen            mDirectoryCache.put(path, rowId);
3254d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen        }
3255afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3256afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        return rowId;
32571717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    }
32581717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
325910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private Cursor getObjectReferences(DatabaseHelper helper, SQLiteDatabase db, int handle) {
326010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
3261a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
3262e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
3263e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
3264e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
3265e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
3266afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long playlistId = c.getLong(0);
3267afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
3268afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
3269e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
3270e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return null;
3271e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
327210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumQueries++;
3273e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                return db.rawQuery(OBJECT_REFERENCES_QUERY,
3274afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        new String[] { Long.toString(playlistId) } );
3275e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3276e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
3277a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c);
3278e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3279e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return null;
3280e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
3281e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
328210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int setObjectReferences(DatabaseHelper helper, SQLiteDatabase db,
328310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            int handle, ContentValues values[]) {
3284e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // first look up the media table and media ID for the object
3285e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        long playlistId = 0;
328610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
3287a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
3288e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
3289e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
3290e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
3291e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
3292afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
3293afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
3294e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
3295e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return 0;
3296e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3297afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                playlistId = c.getLong(0);
3298e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3299e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
3300a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c);
3301e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3302e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (playlistId == 0) {
3303e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            return 0;
3304e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3305e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3306e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // next delete any existing entries
330710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumDeletes++;
330810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        db.delete("audio_playlists_map", "playlist_id=?",
3309e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] { Long.toString(playlistId) });
3310e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3311e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // finally add the new entries
3312e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int count = values.length;
3313e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int added = 0;
3314e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        ContentValues[] valuesList = new ContentValues[count];
3315e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        for (int i = 0; i < count; i++) {
3316e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // convert object ID to audio ID
3317e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long audioId = 0;
3318e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID);
331910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
3320a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            c = db.query("files", sMediaTableColumns, "_id=?",
3321e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    new String[] {  Long.toString(objectId) },
3322e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    null, null, null);
3323e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            try {
3324e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null && c.moveToNext()) {
3325afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    int mediaType = c.getInt(1);
332650d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood                    if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) {
3327e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        // we only allow audio files in playlists, so skip
3328e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        continue;
3329e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    }
3330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    audioId = c.getLong(0);
3331e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3332e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            } finally {
3333a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(c);
3334e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3335e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (audioId != 0) {
3336e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                ContentValues v = new ContentValues();
3337e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId);
3338e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
3339a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added);
3340a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                valuesList[added++] = v;
3341e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3342e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3343e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (added < count) {
3344e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // we weren't able to find everything on the list, so lets resize the array
3345e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // and pass what we have.
3346e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            ContentValues[] newValues = new ContentValues[added];
3347e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            System.arraycopy(valuesList, 0, newValues, 0, added);
3348e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            valuesList = newValues;
3349e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3350e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return playlistBulkInsert(db,
3351e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId),
3352e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                valuesList);
3353e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
3354e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3355b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private static final String[] GENRE_LOOKUP_PROJECTION = new String[] {
3356b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres._ID, // 0
3357b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres.NAME, // 1
3358b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    };
3359b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3360b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private void updateGenre(long rowId, String genre) {
3361b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri uri = null;
3362b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Cursor cursor = null;
3363b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri genresUri = MediaStore.Audio.Genres.getContentUri("external");
3364b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        try {
3365b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // see if the genre already exists
3366b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            cursor = query(genresUri, GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?",
3367b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            new String[] { genre }, null);
3368b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor == null || cursor.getCount() == 0) {
3369b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre does not exist, so create the genre in the genre table
3370b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                ContentValues values = new ContentValues();
3371b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                values.put(MediaStore.Audio.Genres.NAME, genre);
3372b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = insert(genresUri, values);
3373b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            } else {
3374b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre already exists, so compute its Uri
3375b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.moveToNext();
3376b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = ContentUris.withAppendedId(genresUri, cursor.getLong(0));
3377b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3378b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (uri != null) {
3379b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = Uri.withAppendedPath(uri, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY);
3380b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3381b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        } finally {
3382a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(cursor);
3383b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3384b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3385b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (uri != null) {
3386b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // add entry to audio_genre_map
3387b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            ContentValues values = new ContentValues();
3388b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
3389b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            insert(uri, values);
3390b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3391b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    }
3392b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
33932dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
33942dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                               ArrayList<Long> notifyRowIds) {
3395b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        final String volumeName = getVolumeName(uri);
3396b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
3397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
3398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3399d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
3400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3402bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
340310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
340410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
340510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
340610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
340710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
340810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStartTime = SystemClock.currentTimeMicro();
340910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
3411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3413b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
341438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String path = null;
3415b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3416b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3417b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
341838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            path = initialValues.getAsString(MediaStore.MediaColumns.DATA);
3419b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3420b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
342138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
342310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
342410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null && match != VOLUMES && match != MTP_CONNECTED) {
3425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
342810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
3429819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null
343010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                : helper.getWritableDatabase());
3431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
3433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
34342dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
34352dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds);
3436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3437b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    MediaDocumentsProvider.onMediaStoreInsert(
3438b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContext(), volumeName, FileColumns.MEDIA_TYPE_IMAGE, rowId);
3439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
3440b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Images.Media.getContentUri(volumeName), rowId);
3441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3445b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
3446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
344710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3448b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
344910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
3451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
3453b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContentUri(volumeName), rowId);
3454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3458b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
3459b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
346010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3461b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
346210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3463b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
3464b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
3465b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
3466b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContentUri(volumeName), rowId);
3467b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
3468b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3469b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
3470b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
34722dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
34732dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_AUDIO, true, notifyRowIds);
3474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3475b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    MediaDocumentsProvider.onMediaStoreInsert(
3476b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContext(), volumeName, FileColumns.MEDIA_TYPE_AUDIO, rowId);
3477b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = ContentUris.withAppendedId(
3478b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Audio.Media.getContentUri(volumeName), rowId);
3479b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    if (genre != null) {
3480b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        updateGenre(rowId, genre);
3481b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    }
3482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
3487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3488bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
349010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3491ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_genres_map", "genre_id", values);
3492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
3499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3500bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
350210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
3504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
3505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
351210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3513bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                rowId = db.insert("audio_genres", "audio_id", initialValues);
3514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3515b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = ContentUris.withAppendedId(
3516b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Audio.Genres.getContentUri(volumeName), rowId);
3517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
3522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
3523bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
352510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
3527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
3534bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
35362dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, values,
35372dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_PLAYLIST, true, notifyRowIds);
3538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3539b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = ContentUris.withAppendedId(
3540b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Audio.Playlists.getContentUri(volumeName), rowId);
3541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
3547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
3548bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
355010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3551ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_playlists_map", "playlist_id", values);
3552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
35592dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
35602dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds);
3561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3562b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    MediaDocumentsProvider.onMediaStoreInsert(
3563b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContext(), volumeName, FileColumns.MEDIA_TYPE_VIDEO, rowId);
3564b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = ContentUris.withAppendedId(
3565b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Video.Media.getContentUri(volumeName), rowId);
3566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3570c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case AUDIO_ALBUMART: {
357110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                if (helper.mInternal) {
3572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
3573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3574bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = null;
3575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
3576bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
3577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
3578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
3579bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = initialValues;
3580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
358110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3582801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
3583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3587c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
35905619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            {
35915619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                String name = initialValues.getAsString("name");
35925619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                Uri attachedVolume = attachVolume(name);
35935619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) {
35945619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    DatabaseHelper dbhelper = getDatabaseForUri(attachedVolume);
35955619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    if (dbhelper == null) {
35965619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                        Log.e(TAG, "no database for attached volume " + attachedVolume);
35975619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    } else {
35985619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                        dbhelper.mScanStartTime = SystemClock.currentTimeMicro();
35995619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    }
36005619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                }
36015619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                return attachedVolume;
36025619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            }
3603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3604819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            case MTP_CONNECTED:
360534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                synchronized (mMtpServiceConnection) {
360634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    if (mMtpService == null) {
360734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        Context context = getContext();
360834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        // MTP is connected, so grab a connection to MtpService
360934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        context.bindService(new Intent(context, MtpService.class),
361034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                                mMtpServiceConnection, Context.BIND_AUTO_CREATE);
361134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    }
361234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
3613819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                break;
3614819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
3615afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FILES:
361610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
36172dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_NONE, true, notifyRowIds);
3618fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                if (rowId > 0) {
3619b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = Files.getContentUri(volumeName, rowId);
3620fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                }
3621fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                break;
3622fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood
3623e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
36242dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                // We don't send a notification if the insert originated from MTP
362510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
36262dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_NONE, false, notifyRowIds);
3627afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (rowId > 0) {
3628b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = Files.getMtpObjectsUri(volumeName, rowId);
36295d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                }
36305d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                break;
36315d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
3632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
3634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
363638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path != null && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
363738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            // need to set the media_type of all the files below this folder to 0
363838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processNewNoMediaPath(helper, db, path);
363938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
3640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
3641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
364338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
364438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Sets the media type of all files below the newly added .nomedia file or
364538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * hidden folder to 0, so the entries no longer appear in e.g. the audio and
364638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * images views.
364738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     *
364838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * @param path The path to the new .nomedia file or hidden directory
364938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
36505461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    private void processNewNoMediaPath(final DatabaseHelper helper, final SQLiteDatabase db,
36515461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            final String path) {
36525461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        final File nomedia = new File(path);
365338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (nomedia.exists()) {
36545461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            hidePath(helper, db, path);
36555461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        } else {
36565461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            // File doesn't exist. Try again in a little while.
36575461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            // XXX there's probably a better way of doing this
36585461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            new Thread(new Runnable() {
36595461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                @Override
36605461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                public void run() {
36615461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    SystemClock.sleep(2000);
36625461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    if (nomedia.exists()) {
36635461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        hidePath(helper, db, path);
36645461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    } else {
36655461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        Log.w(TAG, "does not exist: " + path, new Exception());
36665461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    }
36675461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                }}).start();
366838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
366938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
367038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
36715461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    private void hidePath(DatabaseHelper helper, SQLiteDatabase db, String path) {
3672b61d1aeb2562bf32b97c4719edc8c4edfdc87d65Marco Nelissen        // a new nomedia path was added, so clear the media paths
3673b61d1aeb2562bf32b97c4719edc8c4edfdc87d65Marco Nelissen        MediaScanner.clearMediaPathCache(true /* media */, false /* nomedia */);
36745461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        File nomedia = new File(path);
36755461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        String hiddenroot = nomedia.isDirectory() ? path : nomedia.getParent();
36765461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        ContentValues mediatype = new ContentValues();
36775461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        mediatype.put("media_type", 0);
36785461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        int numrows = db.update("files", mediatype,
3679a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                "_data >= ? AND _data < ?",
3680a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang                new String[] { hiddenroot  + "/", hiddenroot + "0"});
36815461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        helper.mNumUpdates += numrows;
36825461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        ContentResolver res = getContext().getContentResolver();
36835461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        res.notifyChange(Uri.parse("content://media/"), null);
36845461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    }
36855461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen
368638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
368738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Rescan files for missing metadata and set their type accordingly.
368838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * There is code for detecting the removal of a nomedia file or renaming of
368938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * a directory from hidden to non-hidden in the MediaScanner and MtpDatabase,
369038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * both of which call here.
369138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
369238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private void processRemovedNoMediaPath(final String path) {
3693b61d1aeb2562bf32b97c4719edc8c4edfdc87d65Marco Nelissen        // a nomedia path was removed, so clear the nomedia paths
3694b61d1aeb2562bf32b97c4719edc8c4edfdc87d65Marco Nelissen        MediaScanner.clearMediaPathCache(false /* media */, true /* nomedia */);
369538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        final DatabaseHelper helper;
369638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path.startsWith(mExternalStoragePaths[0])) {
369738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
369838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        } else {
369938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
370038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
370138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
370238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        new ScannerClient(getContext(), db, path);
370338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
370438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
370538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private static final class ScannerClient implements MediaScannerConnectionClient {
370638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String mPath = null;
370738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        MediaScannerConnection mScannerConnection;
370838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase mDb;
370938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
371038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public ScannerClient(Context context, SQLiteDatabase db, String path) {
371138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mDb = db;
371238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mPath = path;
371338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection = new MediaScannerConnection(context, this);
371438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.connect();
371538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
371638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
371738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
371838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onMediaScannerConnected() {
37195461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            Cursor c = mDb.query("files", openFileColumns,
3720a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                    "_data >= ? AND _data < ?",
3721a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang                    new String[] { mPath + "/", mPath + "0"},
37225461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    null, null, null);
3723a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            try  {
3724a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                while (c.moveToNext()) {
3725a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    String d = c.getString(0);
3726a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    File f = new File(d);
3727a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    if (f.isFile()) {
3728a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        mScannerConnection.scanFile(d, null);
3729a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    }
373038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                }
3731a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                mScannerConnection.disconnect();
3732a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            } finally {
3733a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(c);
373438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            }
373538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
373638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
373738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
373838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onScanCompleted(String path, Uri uri) {
373938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
374038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
374138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3742cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    @Override
3743cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
3744cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                throws OperationApplicationException {
3745cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3746cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // The operations array provides no overall information about the URI(s) being operated
3747cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // on, so begin a transaction for ALL of the databases.
3748cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
3749cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
3750cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase idb = ihelper.getWritableDatabase();
3751cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        idb.beginTransaction();
3752cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase edb = null;
3753cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (ehelper != null) {
3754cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb = ehelper.getWritableDatabase();
3755cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb.beginTransaction();
3756cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3757cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        try {
3758cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentProviderResult[] result = super.applyBatch(operations);
3759cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.setTransactionSuccessful();
3760cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3761cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.setTransactionSuccessful();
3762cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3763cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // Rather than sending targeted change notifications for every Uri
3764cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // affected by the batch operation, just invalidate the entire internal
3765cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // and external name space.
3766cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentResolver res = getContext().getContentResolver();
3767cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
3768cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            return result;
3769cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        } finally {
3770cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.endTransaction();
3771cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3772cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.endTransaction();
3773cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3774cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3775cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    }
3776cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3777cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
37789299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) {
3779b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
3780e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            MediaThumbRequest req = null;
3781e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            try {
3782e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                req = new MediaThumbRequest(
37839299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        getContext().getContentResolver(), path, uri, priority, magic);
3784e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                mMediaThumbQueue.add(req);
3785e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                // Trigger the handler.
3786e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
3787e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                msg.sendToTarget();
3788e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            } catch (Throwable t) {
3789e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Log.w(TAG, t);
3790e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            }
3791b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
3792b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
3793b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
3794b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
3796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
3797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
3798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
3799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
3801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
3802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
3803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
3804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
38059be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            return mExternalStoragePaths[0] + "/" + directoryName + "/" + name + preferredExtension;
3806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
38096ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen    private boolean ensureFileExists(Uri uri, String path) {
3810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
3811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
3812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
3813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
38146ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen            try {
38156ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen                checkAccess(uri, file,
38166ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen                        ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE);
38176ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen            } catch (FileNotFoundException e) {
38186ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen                return false;
38196ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen            }
3820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
3821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
3822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
3823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
3824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
3825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
3826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
3827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
382817ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood            file.getParentFile().mkdirs();
3829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
383017ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood                return file.createNewFile();
3831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
3832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
3833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
3835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
3839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
3840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
3841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
3844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
3845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
3847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
3848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
3849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
38509f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
3851afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3852afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE;
38539f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
38549f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
3855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
3856afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
3858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3860b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
3861b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3862b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
3863b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
3864b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3865b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
3867afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3868afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO;
3869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
3872afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
3877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
3882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
3885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
3886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
3888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
3893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
3896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
3899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
3903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
3908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
3910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
3913afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3914afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST;
3915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3918afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
3923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
3925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
3928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
3930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
3931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
3934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
3935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
3936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
3939afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3940afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO;
3941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
3944afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3948b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
3949b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3950b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
3951b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
3952b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3953b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
395416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
3955e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
39561717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                where = "_id=" + uri.getPathSegments().get(2);
395716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
3958e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
395916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                out.table = "files";
39601717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                break;
39611717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
3962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
3965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
3968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
3969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
3970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
3971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
3972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
3973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
3976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
3980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
398101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        uri = safeUncanonicalize(uri);
3982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
3984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
3988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
3989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
399010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
399110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
399210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
399310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
399410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
399510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStopTime = SystemClock.currentTimeMicro();
3996988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                String msg = dump(database, false);
3997988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                logToDb(database.getWritableDatabase(), msg);
399810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
4000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
4001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4003819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        if (match == VOLUMES_ID) {
4004819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            detachVolume(uri);
4005819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            count = 1;
4006819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else if (match == MTP_CONNECTED) {
400734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (mMtpServiceConnection) {
400834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                if (mMtpService != null) {
400934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // MTP has disconnected, so release our connection to MtpService
401034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    getContext().unbindService(mMtpServiceConnection);
401134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 1;
401234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // mMtpServiceConnection.onServiceDisconnected might not get called,
401334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // so set mMtpService = null here
401434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
401534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } else {
401634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 0;
401734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
401834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
4019819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else {
4020b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey            final String volumeName = getVolumeName(uri);
4021b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
4022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
4023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
4024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
4025819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                        "Unknown URI: " + uri + " match: " + match);
4026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
402710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            database.mNumDeletes++;
4028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
4029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
4031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
4032a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                if (sGetTableAndWhereParam.table.equals("files")) {
4033d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
4034d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    if (deleteparam == null || ! deleteparam.equals("false")) {
4035d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        database.mNumQueries++;
4036d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
4037d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sMediaTypeDataId,
4038d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
4039d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        String [] idvalue = new String[] { "" };
40404eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        String [] playlistvalues = new String[] { "", "" };
4041a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        try {
4042a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                            while (c.moveToNext()) {
4043a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                final int mediaType = c.getInt(0);
4044a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                final String data = c.getString(1);
4045a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                final long id = c.getLong(2);
4046b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
4047a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                if (mediaType == FileColumns.MEDIA_TYPE_IMAGE) {
4048a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    deleteIfAllowed(uri, data);
4049b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                    MediaDocumentsProvider.onMediaStoreDelete(getContext(),
4050a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                            volumeName, FileColumns.MEDIA_TYPE_IMAGE, id);
4051b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
4052b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                    idvalue[0] = String.valueOf(id);
4053a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    database.mNumQueries++;
4054a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    Cursor cc = db.query("thumbnails", sDataOnlyColumn,
4055a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                "image_id=?", idvalue, null, null, null);
4056a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    try {
4057a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        while (cc.moveToNext()) {
4058a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                            deleteIfAllowed(uri, cc.getString(0));
4059a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        }
4060a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        database.mNumDeletes++;
4061a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        db.delete("thumbnails", "image_id=?", idvalue);
4062a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    } finally {
4063a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        IoUtils.closeQuietly(cc);
4064a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    }
4065a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                } else if (mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
4066a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    deleteIfAllowed(uri, data);
4067a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    MediaDocumentsProvider.onMediaStoreDelete(getContext(),
4068a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                            volumeName, FileColumns.MEDIA_TYPE_VIDEO, id);
4069a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson
4070a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                } else if (mediaType == FileColumns.MEDIA_TYPE_AUDIO) {
4071a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    if (!database.mInternal) {
4072a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        MediaDocumentsProvider.onMediaStoreDelete(getContext(),
4073a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                volumeName, FileColumns.MEDIA_TYPE_AUDIO, id);
4074a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson
4075a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        idvalue[0] = String.valueOf(id);
4076a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        database.mNumDeletes += 2; // also count the one below
4077a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        db.delete("audio_genres_map", "audio_id=?", idvalue);
4078a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        // for each playlist that the item appears in, move
4079a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        // all the items behind it forward by one
4080a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        Cursor cc = db.query("audio_playlists_map",
4081a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                    sPlaylistIdPlayOrder,
4082a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                    "audio_id=?", idvalue, null, null, null);
4083a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        try {
4084a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                            while (cc.moveToNext()) {
4085a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                playlistvalues[0] = "" + cc.getLong(0);
4086a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                playlistvalues[1] = "" + cc.getInt(1);
4087a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                database.mNumUpdates++;
4088a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                db.execSQL("UPDATE audio_playlists_map" +
4089a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                        " SET play_order=play_order-1" +
4090a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                        " WHERE playlist_id=? AND play_order>?",
4091a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                        playlistvalues);
4092a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                            }
4093a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                            db.delete("audio_playlists_map", "audio_id=?", idvalue);
4094a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        } finally {
4095a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                            IoUtils.closeQuietly(cc);
4096a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                        }
40974eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    }
4098a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                } else if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
4099a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    // TODO, maybe: remove the audio_playlists_cleanup trigger and
4100a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    // implement functionality here (clean up the playlist map)
4101f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                }
41025afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen                            }
4103a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        } finally {
4104a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                            IoUtils.closeQuietly(c);
4105a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                        }
4106a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    }
4107a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                }
4108a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
4109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
411036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    case MTP_OBJECTS:
4111e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood                    case MTP_OBJECTS_ID:
4112d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        try {
4113d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            // don't send objectRemoved event since this originated from MTP
4114d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = true;
411510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            database.mNumDeletes++;
411610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            count = db.delete("files", sGetTableAndWhereParam.where, whereArgs);
4117d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        } finally {
4118d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = false;
4119d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        }
412036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        break;
412178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    case AUDIO_GENRES_ID_MEMBERS:
412210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
412378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        count = db.delete("audio_genres_map",
412478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
412578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        break;
4126166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
4127a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS_ID:
4128a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS:
4129166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS_ID:
4130166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS:
4131166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        // Delete the referenced files first.
4132166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
4133166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sDataOnlyColumn,
4134166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
4135166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        if (c != null) {
4136a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                            try {
4137a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                while (c.moveToNext()) {
4138a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    deleteIfAllowed(uri, c.getString(0));
4139a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                }
4140a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                            } finally {
4141a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                IoUtils.closeQuietly(c);
4142166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            }
4143166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        }
4144166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        database.mNumDeletes++;
4145166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        count = db.delete(sGetTableAndWhereParam.table,
4146166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
4147166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        break;
4148166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
4149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
415010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
4151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
4152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
4153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
4154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4155b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
41563631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // Since there are multiple Uris that can refer to the same files
41573631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // and deletes can affect other objects in storage (like subdirectories
41583631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // or playlists) we will notify a change on the entire volume to make
41593631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // sure no listeners miss the notification.
4160b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volumeName);
41613631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                getContext().getContentResolver().notifyChange(notifyUri, null);
4162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
4166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
416938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    public Bundle call(String method, String arg, Bundle extras) {
417038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (MediaStore.UNHIDE_CALL.equals(method)) {
417138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processRemovedNoMediaPath(arg);
417238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            return null;
417338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
417438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        throw new UnsupportedOperationException("Unsupported call: " + method);
417538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
417638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
417738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    @Override
4178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
4179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
418001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        uri = safeUncanonicalize(uri);
4181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
4182b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
4183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
418410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
418510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
4186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
4187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
4188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
418910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumUpdates++;
419010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
419110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
4192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4193b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
4194b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
4195b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
4196b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
4197b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
4198b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
4199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
4200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
4201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
42021d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // special case renaming directories via MTP.
42031d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // in this case we must update all paths in the database with
42041d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // the directory name as a prefix
42051d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID)
42061d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    && initialValues != null && initialValues.size() == 1) {
42071d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                String oldPath = null;
4208801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA);
42097f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.remove(newPath);
42101d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                // MtpDatabase will rename the directory first, so we test the new file name
421138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(newPath);
421238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (newPath != null && f.isDirectory()) {
421310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumQueries++;
42141d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION,
42151d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        userWhere, whereArgs, null, null, null);
42161d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    try {
42171d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null && cursor.moveToNext()) {
42181d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            oldPath = cursor.getString(1);
42191d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
42201d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    } finally {
4221a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        IoUtils.closeQuietly(cursor);
42221d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
42231d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    if (oldPath != null) {
42247f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                        mDirectoryCache.remove(oldPath);
42251d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        // first rename the row for the directory
422610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
42271d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        count = db.update(sGetTableAndWhereParam.table, initialValues,
42281d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
42291d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0) {
42309bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                            // update the paths of any files and folders contained in the directory
42313fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                            Object[] bindArgs = new Object[] {
42323fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    newPath,
42333fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    oldPath.length() + 1,
42343fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    oldPath + "/",
42353fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    oldPath + "0",
42369bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    // update bucket_display_name and bucket_id based on new path
42379bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    f.getName(),
42389bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    f.toString().toLowerCase().hashCode()
42399bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    };
424010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
42415461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                            db.execSQL("UPDATE files SET _data=?1||SUBSTR(_data, ?2)" +
42429bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    // also update bucket_display_name
42433fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    ",bucket_display_name=?5" +
42443fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    ",bucket_id=?6" +
4245a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                                    " WHERE _data >= ?3 AND _data < ?4;",
42465461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    bindArgs);
42471d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
42481d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
42491d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0 && !db.inTransaction()) {
42501d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            getContext().getContentResolver().notifyChange(uri, null);
42511d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
425238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        if (f.getName().startsWith(".")) {
425338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            // the new directory name is hidden
425438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            processNewNoMediaPath(helper, db, newPath);
425538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        }
42561d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        return count;
42571d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
425838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                } else if (newPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
425938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    processNewNoMediaPath(helper, db, newPath);
42601d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                }
42611d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            }
42621d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
4263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
4264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
4265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
4266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
4267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
42682658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
42692658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
42702658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        values.remove(MediaStore.Audio.Media.COMPILATION);
427107656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen
4272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
4273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
4274a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
42756006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("artist");
4276a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
4277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
427810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> artistCache = helper.mArtistCache;
4279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
4280a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
4281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
428210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    artistRowId = getKeyIdForName(helper, db,
428310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "artists", "artist_key", "artist",
4284acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
4285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
4286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
4287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
4288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
4290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
429259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
4293a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
42946006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("album");
4295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
4296801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                            String path = values.getAsString(MediaStore.MediaColumns.DATA);
429759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
42982658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            if (albumartist != null) {
42992658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                albumHash = albumartist.hashCode();
43002658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            } else if (compilation != null && compilation.equals("1")) {
43012658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                // nothing to do, hash already set
430259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
43039289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path == null) {
43049289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    if (match == AUDIO_MEDIA) {
43059289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Log.w(TAG, "Possible multi row album name update without"
43069289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                + " path could give wrong album key");
43079289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    } else {
43089289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        //Log.w(TAG, "Specify path to avoid extra query");
43099289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Cursor c = query(uri,
43109289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                new String[] { MediaStore.Audio.Media.DATA},
43119289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                null, null, null);
43129289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        if (c != null) {
43139289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            try {
43149289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                int numrows = c.getCount();
43159289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                if (numrows == 1) {
43169289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    c.moveToFirst();
43179289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    path = c.getString(0);
43189289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                } else {
43199289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    Log.e(TAG, "" + numrows + " rows for " + uri);
43209289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                }
43219289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            } finally {
4322a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                                IoUtils.closeQuietly(c);
43239289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            }
43249289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        }
43259289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    }
43269289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
43279289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path != null) {
43289289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
43299289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
433059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
43312658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
4332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
4333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
433410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> albumCache = helper.mAlbumCache;
4335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
433659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
433759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
4338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
433910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    albumRowId = getKeyIdForName(helper, db,
434010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "albums", "album_key", "album",
4341a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
4342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
4343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
4344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
4345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
4347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
4350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
4351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
4352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
4353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
4354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
4355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
4356e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
4357e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
4358e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
4359e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
4360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
436210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
4363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        count = db.update(sGetTableAndWhereParam.table, values,
4364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
4365b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        if (genre != null) {
4366b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            if (count == 1 && match == AUDIO_MEDIA_ID) {
4367b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                long rowId = Long.parseLong(uri.getPathSegments().get(3));
4368b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                updateGenre(rowId, genre);
4369b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            } else {
4370b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                // can't handle genres for bulk update or for non-audio files
4371b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                Log.w(TAG, "ignoring genre in update: count = "
4372b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                        + count + " match = " + match);
4373b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            }
4374b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        }
4375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
4378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
4379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
4380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
4381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
4382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
4383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
4384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
4385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
4386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
4387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
4388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
4389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
4390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
4391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
4392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4393b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                        computeTakenTime(values);
439410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
4395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
4396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
439701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
439801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
439901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
440001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
440110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumQueries++;
440201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
440301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
440401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
4405b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
4406216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                try {
4407216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    while (c.moveToNext()) {
4408216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        long magic = c.getLong(2);
4409216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        if (magic == 0) {
4410216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                            requestMediaThumbnail(c.getString(1), uri,
4411216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                                    MediaThumbRequest.PRIORITY_NORMAL, 0);
4412216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        }
4413b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
4414216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                } finally {
4415a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                                    IoUtils.closeQuietly(c);
4416b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
4417b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
4418b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
4419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4421f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4422f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
4423f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    String moveit = uri.getQueryParameter("move");
4424f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    if (moveit != null) {
4425f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
4426f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        if (initialValues.containsKey(key)) {
4427f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int newpos = initialValues.getAsInteger(key);
4428f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            List <String> segments = uri.getPathSegments();
4429f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            long playlist = Long.valueOf(segments.get(3));
4430f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int oldpos = Integer.valueOf(segments.get(5));
443110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            return movePlaylistEntry(helper, db, playlist, oldpos, newpos);
4432f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        }
4433f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        throw new IllegalArgumentException("Need to specify " + key +
4434f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                                " when using 'move' parameter");
4435f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    }
4436f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    // fall through
4437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
443810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumUpdates++;
4439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
4440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
4441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4444cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // in a transaction, the code that began the transaction should be taking
4445cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // care of notifications once it ends the transaction successfully
4446cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (count > 0 && !db.inTransaction()) {
4447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
4448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
4450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
445210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int movePlaylistEntry(DatabaseHelper helper, SQLiteDatabase db,
445310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            long playlist, int from, int to) {
4454f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        if (from == to) {
4455f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return 0;
4456f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
4457f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        db.beginTransaction();
44585e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        int numlines = 0;
4459a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson        Cursor c = null;
4460f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        try {
446110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates += 3;
4462a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            c = db.query("audio_playlists_map",
44634eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    new String [] {"play_order" },
44644eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order",
44654eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    from + ",1");
44664eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.moveToFirst();
44674eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            int from_play_order = c.getInt(0);
4468a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c);
44694eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c = db.query("audio_playlists_map",
44704eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    new String [] {"play_order" },
44714eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order",
44724eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    to + ",1");
44734eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.moveToFirst();
44744eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            int to_play_order = c.getInt(0);
4475f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=-1" +
44764eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    " WHERE play_order=" + from_play_order +
4477f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " AND playlist_id=" + playlist);
4478f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // We could just run both of the next two statements, but only one of
4479f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // of them will actually do anything, so might as well skip the compile
4480f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // and execute steps.
4481f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            if (from  < to) {
4482f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" +
44834eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " WHERE play_order<=" + to_play_order +
44844eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " AND play_order>" + from_play_order +
4485f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4486f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = to - from + 1;
4487f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            } else {
4488f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" +
44894eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " WHERE play_order>=" + to_play_order +
44904eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " AND play_order<" + from_play_order +
4491f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4492f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = from - to + 1;
4493f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            }
44944eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=" + to_play_order +
4495f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=-1 AND playlist_id=" + playlist);
4496f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.setTransactionSuccessful();
4497f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        } finally {
4498f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.endTransaction();
4499a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c);
4500f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
45015e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé
45025e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
45035e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé                .buildUpon().appendEncodedPath(String.valueOf(playlist)).build();
45045e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        // notifyChange() must be called after the database transaction is ended
45055e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        // or the listeners will read the old data in the callback
45065e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        getContext().getContentResolver().notifyChange(uri, null);
45075e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé
45085e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        return numlines;
4509f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    }
4510f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
4512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
4513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
4514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
4516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
4517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
451871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
451901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        uri = safeUncanonicalize(uri);
4520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
452171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
452271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
452371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
452471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
452571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
452671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
452771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
452871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
45295fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            if (db == null) {
45305fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                throw new IllegalStateException("Couldn't open database for " + uri);
45315fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            }
453271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
453371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
453471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
453571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
453671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
453771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
453871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
453971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
454071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
4541a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            try {
4542a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                if (c.moveToFirst()) {
4543a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    String audiopath = c.getString(0);
4544a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    int albumid = c.getInt(1);
4545a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    // Try to get existing album art for this album first, which
4546a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    // could possibly have been obtained from a different file.
4547a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    // If that fails, try to get it from this specific file.
4548a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
4549a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    try {
4550a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        pfd = openFileAndEnforcePathPermissionsHelper(newUri, mode);
4551a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    } catch (FileNotFoundException ex) {
4552a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        // That didn't work, now try to get it from the specific file
4553a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        pfd = getThumb(database, db, audiopath, albumid, null);
4554a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    }
455571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
4556a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            } finally {
4557a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(c);
455871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
455971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
456071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
456171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4563007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            pfd = openFileAndEnforcePathPermissionsHelper(uri, mode);
4564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
456571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
456671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
456771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
456871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
456971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
4571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
4572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
4573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
4574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
4575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
45775fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                if (db == null) {
45785fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                    throw new IllegalStateException("Couldn't open database for " + uri);
45795fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                }
4580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
458271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
4583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
4584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
4585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
4586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
4587a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen                        null, null, null, null, MediaStore.Audio.Media.TRACK);
4588a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                try {
4589a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    if (c.moveToFirst()) {
4590a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        String audiopath = c.getString(0);
4591a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                        pfd = getThumb(database, db, audiopath, albumid, uri);
4592a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    }
4593a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                } finally {
4594a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    IoUtils.closeQuietly(c);
4595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
459771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
459871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
459971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
4600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
4602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4604007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /**
4605007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * Return the {@link MediaColumns#DATA} field for the given {@code Uri}.
4606007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     */
4607007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private File queryForDataFile(Uri uri) throws FileNotFoundException {
4608007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final Cursor cursor = query(
4609007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                uri, new String[] { MediaColumns.DATA }, null, null, null);
46108ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey        if (cursor == null) {
46118ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey            throw new FileNotFoundException("Missing cursor for " + uri);
46128ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey        }
46138ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey
4614007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
4615007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            switch (cursor.getCount()) {
4616007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                case 0:
4617007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    throw new FileNotFoundException("No entry for " + uri);
4618007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                case 1:
4619007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    if (cursor.moveToFirst()) {
4620007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        return new File(cursor.getString(0));
4621007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    } else {
4622007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        throw new FileNotFoundException("Unable to read entry for " + uri);
4623007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    }
4624007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                default:
4625007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    throw new FileNotFoundException("Multiple items at " + uri);
4626007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            }
4627007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } finally {
4628a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(cursor);
4629007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4630007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
4631007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4632007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /**
4633007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * Replacement for {@link #openFileHelper(Uri, String)} which enforces any
4634007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * permissions applicable to the path before returning.
4635007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     */
4636007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, String mode)
4637007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throws FileNotFoundException {
4638a2bd8dfeac57dc09727c6d16ebf9bac1061df23dAdam Lesinski        final int modeBits = ParcelFileDescriptor.parseMode(mode);
4639007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4640f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey        File file = queryForDataFile(uri);
4641b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4642b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        checkAccess(uri, file, modeBits);
4643b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4644b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        // Bypass emulation layer when file is opened for reading, but only
4645b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        // when opening read-only and we have an exact match.
4646b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        if (modeBits == MODE_READ_ONLY) {
4647b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            file = Environment.maybeTranslateEmulatedPathToInternal(file);
4648b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        }
4649b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4650b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        return ParcelFileDescriptor.open(file, modeBits);
4651b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen    }
4652b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4653b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen    private void deleteIfAllowed(Uri uri, String path) {
4654b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        try {
4655b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            File file = new File(path);
4656b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            checkAccess(uri, file, ParcelFileDescriptor.MODE_WRITE_ONLY);
4657b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            file.delete();
4658b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        } catch (Exception e) {
4659b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            Log.e(TAG, "Couldn't delete " + path);
4660b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        }
4661b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen    }
4662b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4663b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen    private void checkAccess(Uri uri, File file, int modeBits) throws FileNotFoundException {
4664b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0;
4665007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final String path;
4666007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
4667007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            path = file.getCanonicalPath();
4668007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } catch (IOException e) {
4669007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throw new IllegalArgumentException("Unable to resolve canonical path for " + file, e);
4670007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4671007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
46726ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen        Context c = getContext();
4673c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen        boolean readGranted = false;
4674c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen        boolean writeGranted = false;
4675c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen        if (isWrite) {
4676c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen            writeGranted =
4677c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen                (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
4678c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen                == PackageManager.PERMISSION_GRANTED);
4679c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen        } else {
4680c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen            readGranted =
46816ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen                (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
46826ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen                == PackageManager.PERMISSION_GRANTED);
4683c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen        }
46846ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen
46855943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen        if (path.startsWith(sExternalPath) || path.startsWith(sLegacyPath)) {
4686007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            if (isWrite) {
4687c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen                if (!writeGranted) {
46883e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                    c.enforceCallingOrSelfPermission(
4689c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen                        WRITE_EXTERNAL_STORAGE, "External path: " + path);
46903e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                }
4691c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen            } else if (!readGranted) {
4692c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen                c.enforceCallingOrSelfPermission(
4693c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen                    READ_EXTERNAL_STORAGE, "External path: " + path);
4694007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            }
4695007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } else if (path.startsWith(sCachePath)) {
4696c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen            if ((isWrite && !writeGranted) || !readGranted) {
4697c72ec166dbe14db3a78a2cd9c0dbbe583a6b7021Marco Nelissen                c.enforceCallingOrSelfPermission(ACCESS_CACHE_FILESYSTEM, "Cache path: " + path);
46986ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            }
4699dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen        } else if (isSecondaryExternalPath(path)) {
4700dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen            // read access is OK with the appropriate permission
47016ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            if (!readGranted) {
4702b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                if (c.checkCallingOrSelfPermission(WRITE_MEDIA_STORAGE)
4703b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                        == PackageManager.PERMISSION_DENIED) {
4704b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                    c.enforceCallingOrSelfPermission(
4705b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                            READ_EXTERNAL_STORAGE, "External path: " + path);
4706b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                }
47076ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            }
4708b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig            if (isWrite) {
4709b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
4710b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                        != PackageManager.PERMISSION_GRANTED) {
4711b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                    c.enforceCallingOrSelfPermission(
4712b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                            WRITE_MEDIA_STORAGE, "External path: " + path);
4713b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig                }
47146ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            }
4715b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig        } else if (isWrite) {
4716b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig            // don't write to non-cache, non-sdcard files.
4717b5267f95579e1a51bd0e8128f99ca6f1635d208bJohan Redestig            throw new FileNotFoundException("Can't access " + file);
471870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        } else {
471970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            checkWorldReadAccess(path);
4720007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4721007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
4722007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4723dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen    private boolean isSecondaryExternalPath(String path) {
4724dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen        for (int i = mExternalStoragePaths.length - 1; i >= 0; --i) {
4725dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen            if (path.startsWith(mExternalStoragePaths[i])) {
4726dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen                return true;
4727dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen            }
4728dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen        }
4729dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen        return false;
4730dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen    }
4731dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen
473270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    /**
473370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen     * Check whether the path is a world-readable file
473470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen     */
473570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    private void checkWorldReadAccess(String path) throws FileNotFoundException {
473670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
473770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        try {
4738f3b67d56f2225dd458f896b15a7b36badfe8be00Elliott Hughes            StructStat stat = Os.stat(path);
473970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            int accessBits = OsConstants.S_IROTH;
474070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            if (OsConstants.S_ISREG(stat.st_mode) &&
474170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                ((stat.st_mode & accessBits) == accessBits)) {
474270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                checkLeadingPathComponentsWorldExecutable(path);
474370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                return;
474470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
474570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        } catch (ErrnoException e) {
474670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            // couldn't stat the file, either it doesn't exist or isn't
474770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            // accessible to us
474870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        }
474970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
475070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        throw new FileNotFoundException("Can't access " + path);
475170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    }
475270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
475370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    private void checkLeadingPathComponentsWorldExecutable(String filePath)
475470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            throws FileNotFoundException {
475570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        File parent = new File(filePath).getParentFile();
475670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
475770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        int accessBits = OsConstants.S_IXOTH;
475870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
475970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        while (parent != null) {
476070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            if (! parent.exists()) {
476170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                // parent dir doesn't exist, give up
476270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                throw new FileNotFoundException("access denied");
476370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
476470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            try {
4765f3b67d56f2225dd458f896b15a7b36badfe8be00Elliott Hughes                StructStat stat = Os.stat(parent.getPath());
476670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                if ((stat.st_mode & accessBits) != accessBits) {
476770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                    // the parent dir doesn't have the appropriate access
476870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                    throw new FileNotFoundException("Can't access " + filePath);
476970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                }
477070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            } catch (ErrnoException e1) {
477170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                // couldn't stat() parent
477270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                throw new FileNotFoundException("Can't access " + filePath);
477370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
477470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            parent = parent.getParentFile();
477570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        }
477670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    }
477770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
4778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
477910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper;
4780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
4781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
4782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
4783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
4784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
478610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void makeThumbAsync(DatabaseHelper helper, SQLiteDatabase db,
478710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String path, long album_id) {
47888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
47898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
47908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
47918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
47928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
47938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
47948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
47968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
47978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
4798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
479910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        d.helper = helper;
4800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
4801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
4802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
4803a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
48048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
48058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
48068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
48078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
48088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
48098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
48108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
48118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
48128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
48138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
48148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
4815b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
4816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
4817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4819f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    //Return true if the artPath is the dir as it in mExternalStoragePaths
4820f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    //for multi storage support
4821f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    private static boolean isRootStorageDir(String artPath) {
4822f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        for ( int i = 0; i < mExternalStoragePaths.length; i++) {
4823f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen            if ((mExternalStoragePaths[i] != null) &&
4824f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                    (artPath.equalsIgnoreCase(mExternalStoragePaths[i])))
4825f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                return true;
4826f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        }
4827f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        return false;
4828f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    }
4829f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen
48308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
48318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
48328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
48338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
4834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
4837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
4838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
4839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
48408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
48418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
4842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
4843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4844d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
48453f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // same directory as the media file, except if that directory is
48463f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // is the root directory of the sd card or the download directory.
4847d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
4848d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
4849d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
4850d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
4851d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
4852d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
48538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
4854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
4855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
4856d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
48573f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String artPath = path.substring(0, lastSlash);
48580fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen                    String dwndir = Environment.getExternalStoragePublicDirectory(
48592f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
4860d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4861d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
4862d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
4863d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
4864d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
4865f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                        } else if (!isRootStorageDir(artPath) &&
4866ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                                !artPath.equalsIgnoreCase(dwndir)) {
4867d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
4868d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
4869d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
4870d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
4871d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4872d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
4873d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
4874d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
4875d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
4876d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
4877d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4878d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
4879d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
4880d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
4881d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
4882d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4883d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
4884d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
4885d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
4886d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
4887d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4888d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
4889d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
4890d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4891d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
4892d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
4893d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4894d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
4895d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4896d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4897d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
4898d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
4899d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
4900d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
4901d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4902d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
49033f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        File file = new File(artPath, bestmatch);
4904d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
4905d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
4906d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
49072c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                compressed = new byte[(int)file.length()];
4908d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
4909d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
4910d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
4911d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
49122c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                            } catch (OutOfMemoryError ex) {
49132c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                Log.w(TAG, ex);
49142c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                compressed = null;
4915d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
4916d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
4917d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
4918d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
49248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
49258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
4926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
49278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
49288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
4929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
49308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
493110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    Uri getAlbumArtOutputUri(DatabaseHelper helper, SQLiteDatabase db, long album_id, Uri albumart_uri) {
49328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
49338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
49348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
49358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
4936801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            Cursor c = query(albumart_uri, new String [] { MediaStore.MediaColumns.DATA },
49378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
4938d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            try {
4939d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null && c.moveToFirst()) {
4940d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    String albumart_path = c.getString(0);
49416ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen                    if (ensureFileExists(albumart_uri, albumart_path)) {
4942d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                        out = albumart_uri;
4943d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    }
4944d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                } else {
4945d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    albumart_uri = null;
4946d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                }
4947d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            } finally {
4948a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(c);
4949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
495071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
495171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
49528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
49538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
49548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
49558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
495610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
4957801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
49588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
49598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
496050c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen                    // ensure the parent directory exists
496150c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen                    String albumart_path = values.getAsString(MediaStore.MediaColumns.DATA);
49626ad966292d7d8871f0def607944ecca0a9869ea5Marco Nelissen                    ensureFileExists(out, albumart_path);
4963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
49648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
49658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
49668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
49678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
49688a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
49698a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
49708a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
49728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
49738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
497420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) throws IOException {
497520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen        OutputStream outstream = null;
49768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
497720e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            outstream = getContext().getContentResolver().openOutputStream(out);
49788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
49808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
49818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
49828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
49838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
498420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                if (!bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream)) {
498520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    throw new IOException("failed to compress bitmap");
498620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                }
4987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
498820e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen        } finally {
498920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            IoUtils.closeQuietly(outstream);
4990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
49918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
49928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
499334fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen    private ParcelFileDescriptor getThumb(DatabaseHelper helper, SQLiteDatabase db, String path,
499434fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen            long album_id, Uri albumart_uri) {
499571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
499634fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen        d.helper = helper;
499771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
499871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
499971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
500071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
500171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
500271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
500371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
500471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
50058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
5006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
50078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
500871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
50098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
50108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
50118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
50128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
50138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
50148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
50158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
50168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
50178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
50188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
50198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
50208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
50218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
502270676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final Resources r = getContext().getResources();
502370676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size);
502470676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) {
50258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
50268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
50278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
50288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
50298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
50308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
50318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
50328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
50338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
50348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
50358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
50368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
50378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
50388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
50398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
50408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
5041a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false);
5042a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    if (nbm != null && nbm != bm) {
5043a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm.recycle();
5044a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm = nbm;
5045a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    }
50468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
50478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
50488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
50498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
50508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
50518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
505271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
50538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
50548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
505571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
505671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
505771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
505871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
505924001394f571b1f0378840cbf299288e4df10508Bjorn Bringert                return ParcelFileDescriptor.fromData(compressed, "albumthumb");
506071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
506171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
506271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
5063a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This one needs to actually be saved on the sd card.
5064a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This is wrapped in a transaction because there are various things
5065a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // that could go wrong while generating the thumbnail, and we only want
5066a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // to update the database when all steps succeeded.
5067a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            d.db.beginTransaction();
506820e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            Uri out = null;
506920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            ParcelFileDescriptor pfd = null;
5070a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            try {
507120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri);
5072a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen
5073a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (out != null) {
5074a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    writeAlbumArt(need_to_recompress, out, compressed, bm);
5075a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    getContext().getContentResolver().notifyChange(MEDIA_URI, null);
507620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    pfd = openFileHelper(out, "r");
5077a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    d.db.setTransactionSuccessful();
5078a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    return pfd;
5079a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                }
508020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            } catch (IOException ex) {
5081a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
5082a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (UnsupportedOperationException ex) {
5083a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
5084a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } finally {
5085a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                d.db.endTransaction();
5086a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (bm != null) {
5087a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    bm.recycle();
508871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
508920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                if (pfd == null && out != null) {
509020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // Thumbnail was not written successfully, delete the entry that refers to it.
509120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // Note that this only does something if getAlbumArtOutputUri() reused an
509220e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // existing entry from the database. If a new entry was created, it will
509320e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // have been rolled back as part of backing out the transaction.
509420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    getContext().getContentResolver().delete(out, null, null);
509520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                }
509671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
50978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
509871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
5099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
5103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
5104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
5105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
5106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
5107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
5108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
510959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
511059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
511159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
5112a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
5113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
5114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
5115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
5116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
5117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
511810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getKeyIdForName(DatabaseHelper helper, SQLiteDatabase db,
511910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String table, String keyField, String nameField,
512059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
5121a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
5122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
5123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
512551cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            rawName = MediaStore.UNKNOWN_STRING;
5126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
5128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
513051cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            // shouldn't happen, since we only get null keys for null inputs
513151cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            Log.e(TAG, "null key", new Exception());
5132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
5133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
513559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
5136e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen        boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName);
513759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
51382658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // To distinguish same-named albums, we append a hash. The hash is based
51392658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // on the "album artist" tag if present, otherwise on the "compilation" tag
51402658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // if present, otherwise on the path.
514159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
514259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
514359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
514459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
514559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
5146a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
514759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
5148a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
5149a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
5150a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
515159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
515259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
5153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
515410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
5155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
5156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
5158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
5159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
5160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
5161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
5162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
5163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
516410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumInserts++;
5165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
516659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
5167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
516810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            makeThumbAsync(helper, db, path, rowId);
5169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
5170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
5171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
5172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
5173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
5174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
5175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
5176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
5177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
5178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
5179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
5180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
5181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
5183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
5184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
5185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
5186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
5187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
5188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
5189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
519010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
5191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
5192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
5193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
5194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
5195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
5196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
5197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
5198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
5199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
5200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
5201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
5202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
5203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
5205a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson            IoUtils.closeQuietly(c);
5206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
520859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
520959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
5210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
5212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
5216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
5217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
5218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
5219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
5220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
5222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
5223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
5224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
5225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
5227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
5228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
5229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
5231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
5232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
5234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
5235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
5236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
5237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
5238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
5239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
5240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
5241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
5242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
5246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
5247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
5248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
5249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
5250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
5251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
5254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
5255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
5260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
5262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
5263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
5265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
52665619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            if (uri.getPathSegments().size() >= 1) {
5267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
5268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
5271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5273fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isMediaDatabaseName(String name) {
5274fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
5275fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5276fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5277fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (EXTERNAL_DATABASE_NAME.equals(name)) {
5278fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5279fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5280168d49e39a356cdbd0fa04c356e3480e413d6f34kwangjung.kim        if (name.startsWith("external-") && name.endsWith(".db")) {
5281fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5282fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5283fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
5284fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
5285fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
5286fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isInternalMediaDatabaseName(String name) {
5287fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
5288fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5289fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5290fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
5291fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
5292fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
5293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
5295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
5296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
5297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
5299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
5300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
530275392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
5303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
5304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
5305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
5308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
5309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
5310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5312993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            Context context = getContext();
531310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper helper;
5314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
531510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true,
5316fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                        false, mObjectRemovedCallback);
5317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
5318993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                if (Environment.isExternalStorageRemovable()) {
53195d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    final StorageVolume actualVolume = mStorageManager.getPrimaryVolume();
53205d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    final int volumeId = actualVolume.getFatVolumeId();
5321993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
5322ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // Must check for failure!
5323ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // If the volume is not (yet) mounted, this will create a new
5324ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // external-ffffffff.db database instead of the one we expect.  Then, if
5325ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // android.process.media is later killed and respawned, the real external
5326ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // database will be attached, containing stale records, or worse, be empty.
53275d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    if (volumeId == -1) {
5328ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        String state = Environment.getExternalStorageState();
5329ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        if (Environment.MEDIA_MOUNTED.equals(state) ||
5330ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
5331ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // This may happen if external storage was _just_ mounted.  It may also
5332ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // happen if the volume ID is _actually_ 0xffffffff, in which case it
5333ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // must be changed since FileUtils::getFatVolumeId doesn't allow for
5334ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // that.  It may also indicate that FileUtils::getFatVolumeId is broken
5335ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // (missing ioctl), which is also impossible to disambiguate.
5336ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            Log.e(TAG, "Can't obtain external volume ID even though it's mounted.");
5337ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        } else {
5338ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            Log.i(TAG, "External volume is not (yet) mounted, cannot attach.");
5339ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        }
5340ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick
5341ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        throw new IllegalArgumentException("Can't obtain external volume ID for " +
5342ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                                volume + " volume.");
5343ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    }
5344ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick
5345993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // generate database name based on volume ID
53465d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    String dbName = "external-" + Integer.toHexString(volumeId) + ".db";
534710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbName, false,
5348fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
53495d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    mVolumeId = volumeId;
5350993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                } else {
5351993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // external database name should be EXTERNAL_DATABASE_NAME
5352993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // however earlier releases used the external-XXXXXXXX.db naming
5353993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // for devices without removable storage, and in that case we need to convert
5354993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // to this new convention
5355993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME);
5356993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (!dbFile.exists()) {
5357993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // find the most recent external database and rename it to
5358993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // EXTERNAL_DATABASE_NAME, and delete any other older
5359993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // external database files
5360993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        File recentDbFile = null;
5361993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        for (String database : context.databaseList()) {
5362ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                            if (database.startsWith("external-") && database.endsWith(".db")) {
5363993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                File file = context.getDatabasePath(database);
5364993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                if (recentDbFile == null) {
5365993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
5366993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else if (file.lastModified() > recentDbFile.lastModified()) {
5367ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                                    context.deleteDatabase(recentDbFile.getName());
5368993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
5369993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else {
5370ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                                    context.deleteDatabase(file.getName());
5371993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                }
5372993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
5373993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
5374993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        if (recentDbFile != null) {
5375993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (recentDbFile.renameTo(dbFile)) {
5376993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.d(TAG, "renamed database " + recentDbFile.getName() +
5377993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
5378993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            } else {
5379993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.e(TAG, "Failed to rename database " + recentDbFile.getName() +
5380993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
5381993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // This shouldn't happen, but if it does, continue using
5382993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // the file under its old name
5383993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                dbFile = recentDbFile;
5384993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
5385993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
5386993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME
5387993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    }
538810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbFile.getName(), false,
5389fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
5390993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                }
5391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
5392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
5393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
539510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            mDatabases.put(volume, helper);
5396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
539710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (!helper.mInternal) {
5398ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                // create default directories (only happens on first boot)
539910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                createDefaultFolders(helper, helper.getWritableDatabase());
5400ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
5401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
54029be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                File[] files = new File(mExternalStoragePaths[0], ALBUM_THUMB_FOLDER).listFiles();
5403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
5404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
5405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
5406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
5409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
5410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
5411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
5412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
5413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
5414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
5415a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    IoUtils.closeQuietly(cursor);
5416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
5419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
5420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
5421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
5422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
5423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
5428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
5429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
5433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
5434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
5435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
5437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
543975392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
5440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
5441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
5442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
5445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
5446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
5447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
5448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
5449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
5450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
5451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
5454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
5455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
5456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
5458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
5459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
5460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
5461e9ee0248d62f3badef8a554f35f78e9116ef8a5cMike Lockwood            } catch (Exception e) {
5462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
5463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
5466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
5467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
5470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
5471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
5474ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer    private static final boolean LOCAL_LOGV = false;
5475971a2ef5165e2072c76bf25049fdda94187019c2Dianne Hackborn
5476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
5477993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private static final String EXTERNAL_DATABASE_NAME = "external.db";
5478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
5480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
5481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
5483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
5484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
5485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
5487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
5489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
5491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
5492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
54930027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    // current FAT volume ID
5494993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private int mVolumeId = -1;
54950027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
5496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
5497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
5498268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen    static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs";
5499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
5501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
5502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
55031717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS
550416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    // are stored in the "files" table, so do not renumber them unless you also add
55051717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // a corresponding database upgrade step for it.
5506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
5507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
5508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
5509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
5510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
5512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
5513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
5514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
5515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
5516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
5517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
5518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
5519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
5520bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen    private static final int AUDIO_GENRES_ALL_MEMBERS = 109;
5521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
5522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
5523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
5524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
5525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
5526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
5527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
5528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
5529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
5530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
5531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
553271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
5533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
5535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
5536b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
5537b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
5538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
5540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
5541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5542a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
5543a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
5544a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
5545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
5547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
55480027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private static final int FS_ID = 600;
5549704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen    private static final int VERSION = 601;
55500027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
555116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES = 700;
555216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES_ID = 701;
5553a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
5554e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    // Used only by the MTP implementation
5555e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS = 702;
5556e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS_ID = 703;
5557e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECT_REFERENCES = 704;
5558819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // UsbReceiver calls insert() and delete() with this URI to tell us
5559819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // when MTP is connected and disconnected
5560819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    private static final int MTP_CONNECTED = 705;
5561b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
5562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
5563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
5564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5565b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
5566b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
5567b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
5568b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
55691d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    private static final String[] PATH_PROJECTION = new String[] {
55701d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood        MediaStore.MediaColumns._ID,
55711d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            MediaStore.MediaColumns.DATA,
55721d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    };
55731d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
5574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
5575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
5576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
5577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
5578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5579b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
5580b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
5581b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
5582b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
5583b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
5584b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
5585e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private static final String OBJECT_REFERENCES_QUERY =
5586afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map"
5587afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?"
5588afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER;
5589e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
5590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
5591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
5592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
5593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
5594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
5595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
5596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
5598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
5599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
5600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
5601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
5602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
5603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
5604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
5605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
5606bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen        URI_MATCHER.addURI("media", "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS);
5607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
5608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
5609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
5610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
5611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
5612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
5613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
5614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
5615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
5616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
5617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
561871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
5619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
5621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
5622b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
5623b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
5624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
5626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
56270027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        URI_MATCHER.addURI("media", "*/fs_id", FS_ID);
5628704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        URI_MATCHER.addURI("media", "*/version", VERSION);
56290027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
5630819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED);
5631819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
5632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
5633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
5634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5635b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        // Used by MTP implementation
563616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file", FILES);
563716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file/#", FILES_ID);
5638e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS);
5639e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID);
5640e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES);
5641b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
5642a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
5643a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
5644a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
5645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
5646a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
5647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
5648a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
5649a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
5650a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
5651a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
5652a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
5653a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
5654a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
5655a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
5656a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
5657a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
5658a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
5659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
566010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
5661b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey    private static String getVolumeName(Uri uri) {
5662b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        final List<String> segments = uri.getPathSegments();
5663b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        if (segments != null && segments.size() > 0) {
5664b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey            return segments.get(0);
5665b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        } else {
5666b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey            return null;
5667b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        }
5668b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey    }
5669b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
567010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    @Override
567110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
567210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        Collection<DatabaseHelper> foo = mDatabases.values();
567310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        for (DatabaseHelper dbh: foo) {
5674988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            writer.println(dump(dbh, true));
5675988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        }
5676988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        writer.flush();
5677988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    }
5678988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
5679988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    private String dump(DatabaseHelper dbh, boolean dumpDbLog) {
5680988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        StringBuilder s = new StringBuilder();
5681988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        s.append(dbh.mName);
5682988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        s.append(": ");
5683988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        SQLiteDatabase db = dbh.getReadableDatabase();
5684988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        if (db == null) {
5685988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append("null");
5686988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        } else {
5687988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append("version " + db.getVersion() + ", ");
5688988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            Cursor c = db.query("files", new String[] {"count(*)"}, null, null, null, null, null);
5689988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            try {
5690988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (c != null && c.moveToFirst()) {
5691988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    int num = c.getInt(0);
5692988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    s.append(num + " rows, ");
5693988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                } else {
5694988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    s.append("couldn't get row count, ");
5695988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5696988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            } finally {
5697a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                IoUtils.closeQuietly(c);
5698988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            }
5699988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumInserts + " inserts, ");
5700988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumUpdates + " updates, ");
5701988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumDeletes + " deletes, ");
5702988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumQueries + " queries, ");
5703988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            if (dbh.mScanStartTime != 0) {
5704988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                s.append("scan started " + DateUtils.formatDateTime(getContext(),
5705988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        dbh.mScanStartTime / 1000,
5706988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        DateUtils.FORMAT_SHOW_DATE
5707988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        | DateUtils.FORMAT_SHOW_TIME
5708988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        | DateUtils.FORMAT_ABBREV_ALL));
5709988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                long now = dbh.mScanStopTime;
5710988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (now < dbh.mScanStartTime) {
5711988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    now = SystemClock.currentTimeMicro();
5712988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5713988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                s.append(" (" + DateUtils.formatElapsedTime(
5714988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        (now - dbh.mScanStartTime) / 1000000) + ")");
5715988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (dbh.mScanStopTime < dbh.mScanStartTime) {
5716988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    if (mMediaScannerVolume != null &&
5717988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            dbh.mName.startsWith(mMediaScannerVolume)) {
5718988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        s.append(" (ongoing)");
571910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    } else {
5720988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        s.append(" (scanning " + mMediaScannerVolume + ")");
5721988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    }
5722988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5723988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            }
5724988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            if (dumpDbLog) {
5725988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                c = db.query("log", new String[] {"time", "message"},
5726f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen                        null, null, null, null, "rowid");
5727988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                try {
5728988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    if (c != null) {
5729988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        while (c.moveToNext()) {
5730988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            String when = c.getString(0);
5731988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            String msg = c.getString(1);
5732988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            s.append("\n" + when + " : " + msg);
5733988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        }
573410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
573510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                } finally {
5736a79fcf1159bef45f99a763d3379f929bb6c13844Mattias Nilsson                    IoUtils.closeQuietly(c);
573710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                }
573810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
573910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        }
5740988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        return s.toString();
574110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    }
5742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
5743