MediaProvider.java revision 2dc83e226f1016c117e9dc377aa6bcbc7328e10e
1702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/*
2702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
3702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
4702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * you may not use this file except in compliance with the License.
6702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * You may obtain a copy of the License at
7702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
8702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
10702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * See the License for the specific language governing permissions and
14702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * limitations under the License.
15702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
16702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
17702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpackage com.android.providers.media;
18702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
19007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.ACCESS_CACHE_FILESYSTEM;
20007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.READ_EXTERNAL_STORAGE;
21007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
22f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
23007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
24007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
25702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.app.SearchManager;
26bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.BroadcastReceiver;
27bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ComponentName;
28bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProvider;
29bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderOperation;
30bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderResult;
31bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentResolver;
32bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentUris;
33bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentValues;
34bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Context;
35bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Intent;
36bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.IntentFilter;
37bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.OperationApplicationException;
38bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ServiceConnection;
39ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.content.SharedPreferences;
40bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.UriMatcher;
4190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissenimport android.content.pm.PackageManager.NameNotFoundException;
4270676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.content.res.Resources;
43702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
44ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils;
450027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor;
46702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
47702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
48702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap;
50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory;
51b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.media.MediaFile;
52702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner;
5338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection;
5438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection.MediaScannerConnectionClient;
55b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
5690345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpConstants;
579be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwoodimport android.mtp.MtpStorage;
58702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
59702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
6038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.os.Bundle;
61702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
62702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils;
63702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler;
64ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread;
65702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message;
66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor;
67702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process;
68d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException;
6910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.os.SystemClock;
70c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwoodimport android.os.storage.StorageManager;
711f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwoodimport android.os.storage.StorageVolume;
72ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.preference.PreferenceManager;
73702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns;
74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore;
75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio;
764eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissenimport android.provider.MediaStore.Audio.Playlists;
77a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Linimport android.provider.MediaStore.Files;
7870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Files.FileColumns;
79702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images;
8070676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Images.ImageColumns;
81702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns;
82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video;
83702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
8410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.text.format.DateUtils;
85702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
86702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
87702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
8810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.FileDescriptor;
89702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
90702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
91702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
92702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
9310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.PrintWriter;
94702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.text.Collator;
95cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList;
9610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.util.Collection;
97702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
98702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
99702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
100f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List;
10138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport java.util.Locale;
102b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
1038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
105166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissenimport libcore.io.ErrnoException;
106166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissenimport libcore.io.Libcore;
107166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/**
109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details.
110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the
111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index).  The content visible at content://media/external/...
112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card.
113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider {
115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri MEDIA_URI = Uri.parse("content://media");
116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");
117b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int ALBUM_THUMB = 1;
118b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int IMAGE_THUMB = 2;
119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>();
121d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen    private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>();
122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
123007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /** Resolved canonical path to external storage. */
124007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private static final String sExternalPath;
125007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /** Resolved canonical path to cache storage. */
126007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private static final String sCachePath;
127007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
128007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    static {
129007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
130007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            sExternalPath = Environment.getExternalStorageDirectory().getCanonicalPath();
131007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            sCachePath = Environment.getDownloadCacheDirectory().getCanonicalPath();
132007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } catch (IOException e) {
133007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throw new RuntimeException("Unable to resolve canonical paths", e);
134007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
135007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
136007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
1377f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    // In memory cache of path<->id mappings, to speed up inserts during media scan
1387f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>();
1397f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
1408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
1418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
1428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
1438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
1448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
1458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
14620434e032e498b716f87cce2f23dd646819218bfRay Chen    // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest.
14720434e032e498b716f87cce2f23dd646819218bfRay Chen    private MediaThumbRequest mCurrentThumbRequest = null;
148b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
149b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
150b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
151b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
152f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood    private boolean mCaseInsensitivePaths;
1539be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private static String[] mExternalStoragePaths;
15417ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood
155a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
156a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
157a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
158a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
159a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
160a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
161a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
164a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
166a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
167a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
168a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
169a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
170a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
171a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
172a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
173ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
174ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
175a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
176a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
177a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
178a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
179a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
180a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
181a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
182a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
183a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
184a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
18563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
186a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
187a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
188a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
189a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
190a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
191a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
192a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
193a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
194a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
19563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
19663f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
197e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen            " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" +
19863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
199a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
200a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
20163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
20263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
203a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
204a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTableColumns = new String[] {
20516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            FileColumns._ID,
206afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            FileColumns.MEDIA_TYPE,
2071717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    };
2081717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
2097f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    private static final String[] sIdOnlyColumn = new String[] {
2107f36494e085c26c69cd5925e54028822025eff29Marco Nelissen        FileColumns._ID
2117f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    };
2127f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
213166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    private static final String[] sDataOnlyColumn = new String[] {
214166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        FileColumns.DATA
215166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    };
216166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
217a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTypeDataId = new String[] {
218a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.MEDIA_TYPE,
219a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.DATA,
220a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns._ID
2214eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    };
2224eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen
2234eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    private static final String[] sPlaylistIdPlayOrder = new String[] {
2244eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen        Playlists.Members.PLAYLIST_ID,
2254eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen        Playlists.Members.PLAY_ORDER
2264eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    };
227a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
228a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
229a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
2341f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                StorageVolume storage = (StorageVolume)intent.getParcelableExtra(
2351f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        StorageVolume.EXTRA_STORAGE_VOLUME);
2361f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // If primary external storage is ejected, then remove the external volume
2371f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // notify all cursors backed by data on that volume.
2381f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                if (storage.getPath().equals(mExternalStoragePaths[0])) {
2391f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    detachVolume(Uri.parse("content://media/external"));
2401f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    sFolderArtMap.clear();
2411f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    MiniThumbFile.reset();
2421f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                } else {
2431f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // If secondary external storage is ejected, then we delete all database
2441f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // entries for that storage from the files table.
2451f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    synchronized (mDatabases) {
2461f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        DatabaseHelper database = mDatabases.get(EXTERNAL_VOLUME);
2471f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        Uri uri = Uri.parse("file://" + storage.getPath());
2481f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        if (database != null) {
2491f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            try {
2501f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // Send media scanner started and stopped broadcasts for apps that rely
2511f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // on these Intents for coarse grained media database notifications.
2521f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2531f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
2541f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood
2551f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // don't send objectRemoved events - MTP be sending StorageRemoved anyway
2561f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = true;
2571f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.d(TAG, "deleting all entries for storage " + storage);
2581f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                SQLiteDatabase db = database.getWritableDatabase();
2594b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // First clear the file path to disable the _DELETE_FILE database hook.
2604b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // We do this to avoid deleting files if the volume is remounted while
2614b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // we are still processing the unmount event.
2624b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                ContentValues values = new ContentValues();
2634b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                values.put(Files.FileColumns.DATA, "");
2644b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String where = FileColumns.STORAGE_ID + "=?";
2654b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) };
2664b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                db.update("files", values, where, whereArgs);
2674b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // now delete the records
2684b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                db.delete("files", where, whereArgs);
2694b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // notify on media Uris as well as the files Uri
2704b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2714b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Audio.Media.getContentUri(EXTERNAL_VOLUME), null);
2724b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2734b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Images.Media.getContentUri(EXTERNAL_VOLUME), null);
2744b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2754b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Video.Media.getContentUri(EXTERNAL_VOLUME), null);
2761f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.getContentResolver().notifyChange(
2771f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        Files.getContentUri(EXTERNAL_VOLUME), null);
2781f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } catch (Exception e) {
2791f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.e(TAG, "exception deleting storage entries", e);
2801f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } finally {
2811f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2821f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
2831f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = false;
2841f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            }
2851f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        }
2861f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    }
2871f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                }
288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
292d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    // set to disable sending events when the operation originates from MTP
293d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private boolean mDisableMtpObjectCallbacks;
294d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
295d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final SQLiteDatabase.CustomFunction mObjectRemovedCallback =
296d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                new SQLiteDatabase.CustomFunction() {
297d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void callback(String[] args) {
2987f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // We could remove only the deleted entry from the cache, but that
2997f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // requires the path, which we don't have here, so instead we just
3007f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // clear the entire cache.
3017f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: include the path in the callback and only remove the affected
3027f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // entry from the cache
3037f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            mDirectoryCache.clear();
304d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // do nothing if the operation originated from MTP
305d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mDisableMtpObjectCallbacks) return;
306d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
307d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "object removed " + args[0]);
308d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            IMtpService mtpService = mMtpService;
309d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mtpService != null) {
310d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                try {
311d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    sendObjectRemoved(Integer.parseInt(args[0]));
312d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                } catch (NumberFormatException e) {
313d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e);
314d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
315d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
316d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
317d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
318d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
324fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static final class DatabaseHelper extends SQLiteOpenHelper {
325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
3265524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        final String mName;
327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
328fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final boolean mEarlyUpgrade;
329fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final SQLiteDatabase.CustomFunction mObjectRemovedCallback;
3305524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        boolean mUpgradeAttempted; // Used for upgrade error handling
33110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumQueries;
33210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumUpdates;
33310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumInserts;
33410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumDeletes;
33510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStartTime;
33610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStopTime;
337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
342fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        public DatabaseHelper(Context context, String name, boolean internal,
343fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                boolean earlyUpgrade,
344fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                SQLiteDatabase.CustomFunction objectRemovedCallback) {
34590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            super(context, name, null, getDatabaseVersion(context));
346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
3475524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mName = name;
348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
349fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mEarlyUpgrade = earlyUpgrade;
350fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mObjectRemovedCallback = objectRemovedCallback;
3510e2a2386b39972286df21f4db5a9dd1df548c34dJeff Brown            setWriteAheadLoggingEnabled(true);
352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
35990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, 0, getDatabaseVersion(mContext));
360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
3685524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = true;
36990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, oldV, newV);
370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
372db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        @Override
373db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        public synchronized SQLiteDatabase getWritableDatabase() {
3745524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            SQLiteDatabase result = null;
3755524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = false;
3765524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            try {
3775524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3785524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            } catch (Exception e) {
3795524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                if (!mUpgradeAttempted) {
3805524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    Log.e(TAG, "failed to open database " + mName, e);
3815524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    return null;
3825524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                }
3835524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3845524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
3855524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // If we failed to open the database during an upgrade, delete the file and try again.
3865524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // This will result in the creation of a fresh database, which will be repopulated
3875524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // when the media scanner runs.
3885524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            if (result == null && mUpgradeAttempted) {
3895524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                mContext.getDatabasePath(mName).delete();
3905524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3915524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3925524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            return result;
3935524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        }
3945524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
396993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * For devices that have removable storage, we support keeping multiple databases
397993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * to allow users to switch between a number of cards.
398993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * On such devices, touch this particular database and garbage collect old databases.
399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
40436d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky
405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
407fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mEarlyUpgrade) return; // Doing early upgrade.
408fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
409fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mObjectRemovedCallback != null) {
410fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback);
411fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            }
412d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
413993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            // the code below is only needed on devices with removable storage
414993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            if (!Environment.isExternalStorageRemovable()) return;
415993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
47634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood    // synchronize on mMtpServiceConnection when accessing mMtpService
477d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private IMtpService mMtpService;
478d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
479d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final ServiceConnection mMtpServiceConnection = new ServiceConnection() {
480d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood         public void onServiceConnected(ComponentName className, android.os.IBinder service) {
48134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
48234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = IMtpService.Stub.asInterface(service);
48334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
484d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
485d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
486d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void onServiceDisconnected(ComponentName className) {
48734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
48834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = null;
48934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
490d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
491d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
492d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
493ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    private static final String[] sDefaultFolderNames = {
494ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MUSIC,
495ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PODCASTS,
496ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_RINGTONES,
497ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_ALARMS,
498ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_NOTIFICATIONS,
499ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PICTURES,
500ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MOVIES,
501ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DOWNLOADS,
502ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DCIM,
503ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    };
504ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
505ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    // creates default folders (Music, Downloads, etc)
50610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void createDefaultFolders(DatabaseHelper helper, SQLiteDatabase db) {
507ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // Use a SharedPreference to ensure we only do this once.
508ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // We don't want to annoy the user by recreating the directories
509ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // after she has deleted them.
510ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
511ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        if (prefs.getInt("created_default_folders", 0) == 0) {
512ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            for (String folderName : sDefaultFolderNames) {
513ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                File file = Environment.getExternalStoragePublicDirectory(folderName);
514ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                if (!file.exists()) {
515ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                    file.mkdirs();
51610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    insertDirectory(helper, db, file.getAbsolutePath());
517ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                }
518ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            }
519ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
520ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            SharedPreferences.Editor e = prefs.edit();
521ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.clear();
522ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.putInt("created_default_folders", 1);
523ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.commit();
524ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        }
525ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
526ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
5279311c7ff9d35ca3acc908da3da7a79fbf7a8da6bMarco Nelissen    public static int getDatabaseVersion(Context context) {
52890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        try {
52990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            return context.getPackageManager().getPackageInfo(
53090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    context.getPackageName(), 0).versionCode;
53190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        } catch (NameNotFoundException e) {
53290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            throw new RuntimeException("couldn't get version code for " + context);
53390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
53490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    }
53590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen
536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
538d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        final Context context = getContext();
539d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
540acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
541acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums._ID);
542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
544acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
546acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
551acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
552acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
553acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
554acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.ALBUM_ART);
555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
55663f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
55763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
558d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "%1", context.getString(R.string.artist_label));
559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
564d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        context.registerReceiver(mUnmountReceiver, iFilter);
565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5667d4f72d8ab71e28e9d40da87ec8c75ded254dd08Dianne Hackborn        mCaseInsensitivePaths = true;
5674f2186758ee1c6eaa702bf1511b233b26143b631Mike Lockwood
568c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        StorageManager storageManager =
569c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood                (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
570c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        mExternalStoragePaths = storageManager.getVolumePaths();
5719be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
579ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND);
580ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        ht.start();
581ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        mThumbHandler = new Handler(ht.getLooper()) {
582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
584b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
585b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
58620434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest = mMediaThumbQueue.poll();
587b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
58820434e032e498b716f87cce2f23dd646819218bfRay Chen                    if (mCurrentThumbRequest == null) {
589b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
590b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
591b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
59220434e032e498b716f87cce2f23dd646819218bfRay Chen                            File origFile = new File(mCurrentThumbRequest.mPath);
5934d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            if (origFile.exists() && origFile.length() > 0) {
59420434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.execute();
5954d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            } else {
5964d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                // original file hasn't been stored yet
5974d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                synchronized (mMediaThumbQueue) {
59820434e032e498b716f87cce2f23dd646819218bfRay Chen                                    Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath);
5994d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
6004d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
601b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
6021d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
6031d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                        } catch (UnsupportedOperationException ex) {
6041d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // This could happen if we unplug the sd card during insert/update/delete
6051d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // See getDatabaseForUri.
6061d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
60722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                        } catch (OutOfMemoryError err) {
60822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            /*
60922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Note: Catching Errors is in most cases considered
61022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * bad practice. However, in this case it is
61122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * motivated by the fact that corrupt or very large
61222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * images may cause a huge allocation to be
61322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * requested and denied. The bitmap handling API in
61422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Android offers no other way to guard against
61522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * these problems than by catching OutOfMemoryError.
61622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             */
61722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            Log.w(TAG, err);
618b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
61920434e032e498b716f87cce2f23dd646819218bfRay Chen                            synchronized (mCurrentThumbRequest) {
62020434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE;
62120434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.notifyAll();
622b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
623b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
624b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
625b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
626b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
627b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
628b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
629b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    makeThumbInternal(d);
632b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
633b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
634b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
642afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String IMAGE_COLUMNS =
643afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_size,_display_name,mime_type,title,date_added," +
644afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
645bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name," +
646bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "width,height";
647bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
648bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String IMAGE_COLUMNSv407 =
649bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_size,_display_name,mime_type,title,date_added," +
650bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
651afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name";
652afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
653805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv99 =
654afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added," +
655afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
656afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
657afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark";
658afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
659805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv100 =
660805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "_data,_display_name,_size,mime_type,date_added," +
661805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
662805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
663805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "bookmark,album_artist";
664805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen
665957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang    private static final String AUDIO_COLUMNSv405 =
666957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "_data,_display_name,_size,mime_type,date_added,is_drm," +
667957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
668957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
669957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "bookmark,album_artist";
670957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
671afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String VIDEO_COLUMNS =
672afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
673afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title,duration,artist,album,resolution,description,isprivate,tags," +
674afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
675bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width," +
676bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "height";
677bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
678bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String VIDEO_COLUMNSv407 =
679bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
680bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "title,duration,artist,album,resolution,description,isprivate,tags," +
681bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
682afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic,bucket_id,bucket_display_name, bookmark";
683afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
684afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified";
685afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
69490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    private static void updateDatabase(Context context, SQLiteDatabase db, boolean internal,
695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
69890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        int dbversion = getDatabaseVersion(context);
69990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (toVersion != dbversion) {
70090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " + dbversion);
701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
70395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
707988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        long startTime = SystemClock.currentTimeMicro();
708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
709d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag.
710acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        // We can't downgrade from those revisions, so start over.
711022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // (the initial change to do this was wrong, so now we actually need to start over
712022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // if the database version is 84-89)
713bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair.
714bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // However version 91 was reused in a divergent development path for gingerbread,
715bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // so we need to support upgrades from 91.
716bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Therefore we will only force a reset for versions 92 - 94.
717d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) ||
718bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    (fromVersion >= 92 && fromVersion <= 94)) {
719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
722d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            fromVersion = 63;
723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
745cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
7469ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup");
7479ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup");
7489ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup");
7499ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup");
750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
780b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
800216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                       "_data TEXT UNIQUE NOT NULL," +
801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
84095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
846acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album) AS number_of_albums, " +
847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
860acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
861acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
862acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
863acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
864acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
88278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                // Contains mappings between audio genres and audio files
883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
973acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
974acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.0 shipped with database version 64
975acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
976acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
982403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // In version 66, originally we updateBucketNames(db, "images"),
983403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // but we need to do it in version 89 and therefore save the update here.
984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
995403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
996403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // In version 68, originally we updateBucketNames(db, "video"),
997403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // but we need to do it in version 89 and therefore save the update here.
998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
1001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
1002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
1005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
1006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
1007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
100895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
1010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
1011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
1012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
1013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
1014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
1015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
1016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
1017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
1018e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "artist='" + MediaStore.UNKNOWN_STRING + "' AND " +
1019e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "album='" + MediaStore.UNKNOWN_STRING + "'" +
1020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
1021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
102295ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
1024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
1025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
1026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
1027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
1028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
1029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
1030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
1032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
1033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
1034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
1035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
1036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
103795ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1038acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1039acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.5 shipped with database version 72
1040acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1041acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
10428d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
10438d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
10448d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
10458d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
10468d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
10478d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
10488d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
10498d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
10508d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
10518d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
10528d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
10538d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
10548d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
10558d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
10568d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
1057a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1058a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
1059a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
1060a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
1061a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
1062a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
1063a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
1064a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
1065a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1066a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
1067a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1068a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
1069a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1070a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
1071a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1072a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
1073a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
1074a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
1075a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
1076a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
1077a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
1078a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
1079e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " +
1080a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1081a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1082a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
1083a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1084a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1085a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1086a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
1087a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1088a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1089a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1090a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
1091a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
1092a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
1093e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " +
1094a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1095a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
1096a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
1097a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1098a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1099a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
1100a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
1101a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1102a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1103a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1104a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
1105a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
1106a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
1107a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
1108a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
1109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
1110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
111159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
111259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
111395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
111459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
111559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
111659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
111759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
111815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
111915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
112015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
112115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
112215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
112315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
112415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
112515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
112615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
112715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
112815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
112915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
113015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
113115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
1132b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1133acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1134acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.6 shipped with database version 76
1135acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1136acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1137b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
1138b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
1139b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
1140b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
1141b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
1142b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
1143b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
1144b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
1145b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
1146b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
1147b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1148b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
1149b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1150b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
1151b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
1152b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
1153b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
1154b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
11551769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen
1156acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1157acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.0 and 2.0.1 shipped with database version 77
1158acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1159acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
11601769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        if (fromVersion < 78) {
1161044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force a rescan of the video entries so we can update
11621769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            // latest changed DATE_TAKEN units (in milliseconds).
11631769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            db.execSQL("UPDATE video SET date_modified=0;");
11641769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        }
1165268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
1166acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1167acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.1 shipped with database version 78
1168acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1169acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1170268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        if (fromVersion < 79) {
1171268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // move /sdcard/albumthumbs to
1172268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // /sdcard/Android/data/com.android.providers.media/albumthumbs,
1173268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // and update the database accordingly
1174268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
11759be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String oldthumbspath = mExternalStoragePaths[0] + "/albumthumbs";
11769be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String newthumbspath = mExternalStoragePaths[0] + "/" + ALBUM_THUMB_FOLDER;
1177268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            File thumbsfolder = new File(oldthumbspath);
1178268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            if (thumbsfolder.exists()) {
1179268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                // move folder to its new location
1180268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                File newthumbsfolder = new File(newthumbspath);
1181268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                newthumbsfolder.getParentFile().mkdirs();
1182268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                if(thumbsfolder.renameTo(newthumbsfolder)) {
1183268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    // update the database
1184268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" +
1185268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                            oldthumbspath + "','" + newthumbspath + "');");
1186268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                }
1187268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            }
1188268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        }
1189044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen
1190044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        if (fromVersion < 80) {
1191044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force rescan of image entries to update DATE_TAKEN as UTC timestamp.
1192044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            db.execSQL("UPDATE images SET date_modified=0;");
1193044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        }
11940ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
1195166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen        if (fromVersion < 81 && !internal) {
11960ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete entries starting with /mnt/sdcard. This is for the benefit
11970ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // of users running builds between 2.0.1 and 2.1 final only, since
11980ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // users updating from 2.0 or earlier will not have such entries.
11990ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12000ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // First we need to update the _data fields in the affected tables, since
12010ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // otherwise deleting the entries will also delete the underlying files
12020ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // (via a trigger), and we want to keep them.
12030ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12040ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12050ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12060ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12070ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12080ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
1209216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12100ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Once the paths have been renamed, we can safely delete the entries
12110ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';");
12120ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM images WHERE _data IS '////';");
12130ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM video WHERE _data IS '////';");
12140ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';");
12150ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';");
12160ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_meta WHERE _data  IS '////';");
12170ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data  IS '////';");
12180ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12190ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // rename existing entries starting with /sdcard to /mnt/sdcard
12200ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta" +
12210ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12220ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists" +
12230ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12240ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images" +
12250ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12260ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video" +
12270ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12280ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails" +
12290ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12300ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails" +
12310ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12320ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art" +
12330ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12340ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12350ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete albums and artists, then clear the modification time on songs, which
12360ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // will cause the media scanner to rescan everything, rebuilding the artist and
12370ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // album tables along the way, while preserving playlists.
12380ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // We need this rescan because ICU also changed, and now generates different
12390ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // collation keys
12400ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from albums");
12410ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from artists");
12420ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
12430ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen        }
124484403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen
124584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        if (fromVersion < 82) {
1246acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // recreate this view with the correct "group by" specifier
124784403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("DROP VIEW IF EXISTS artist_info");
124884403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
124984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "SELECT artist_id AS _id, artist, artist_key, " +
1250acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album_key) AS number_of_albums, " +
125184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
125284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "GROUP BY artist_key;");
125384403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        }
1254216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1255acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /* we skipped over version 83, and reverted versions 84, 85 and 86 */
1256ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen
1257ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        if (fromVersion < 87) {
1258ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // The fastscroll thumb needs an index on the strings being displayed,
1259ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // otherwise the queries it does to determine the correct position
1260ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // becomes really inefficient
1261022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);");
1262022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);");
1263022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);");
1264ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        }
1265216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1266acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        if (fromVersion < 88) {
1267acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // Clean up a few more things from versions 84/85/86, and recreate
1268acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // the few things worth keeping from those changes.
1269acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update1;");
1270acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update2;");
1271acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update3;");
1272acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update4;");
1273acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update1;");
1274acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update2;");
1275acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update3;");
1276acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update4;");
127716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP VIEW IF EXISTS album_artists;");
1278acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);");
1279acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);");
1280acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
1281acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
1282acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
1283acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
1284acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        }
1285403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1286fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // In version 89, originally we updateBucketNames(db, "images") and
1287fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames
1288fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        //  for all files and therefore can save the update here.
1289b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1290b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (fromVersion < 91) {
1291bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // Never query by mini_thumb_magic_index
1292bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index");
1293bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1294bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // sort the items by taken date in each bucket
1295bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)");
1296bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)");
1297bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        }
1298bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1299a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
1300d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // Gingerbread ended up going to version 100, but didn't yet have the "files"
1301d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // table, so we need to create that if we're at 100 or lower. This means
1302d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // we won't be able to upgrade pre-release Honeycomb.
1303d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        if (fromVersion <= 100) {
1304afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Remove various stages of work in progress for MTP support
1305afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
1306afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS files");
130716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;");
130816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;");
130916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;");
131016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;");
1311afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;");
1312afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;");
1313afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;");
1314afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;");
1315afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;");
1316afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1317afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create a new table to manage all files in our storage.
1318afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // This contains a union of all the columns from the old
1319afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // images, audio_meta, videos and audio_playlist tables.
1320afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TABLE files (" +
132100a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen                        "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data TEXT," +     // this can be null for playlists
1323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_size INTEGER," +
1324afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "format INTEGER," +
1325afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "parent INTEGER," +
1326afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_added INTEGER," +
1327afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified INTEGER," +
1328afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mime_type TEXT," +
1329afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title TEXT," +
1330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "description TEXT," +
1331afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_display_name TEXT," +
1332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1333afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images
1334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "picasa_id TEXT," +
1335afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "orientation INTEGER," +
1336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images and video
1338afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "latitude DOUBLE," +
1339afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "longitude DOUBLE," +
1340afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken INTEGER," +
1341afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic INTEGER," +
1342afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_id TEXT," +
1343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_display_name TEXT," +
1344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "isprivate INTEGER," +
1345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1346afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio
1347afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title_key TEXT," +
1348afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist_id INTEGER," +
1349afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album_id INTEGER," +
1350afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "composer TEXT," +
1351afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track INTEGER," +
1352afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "year INTEGER CHECK(year!=0)," +
1353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_ringtone INTEGER," +
1354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_music INTEGER," +
1355afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_alarm INTEGER," +
1356afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_notification INTEGER," +
1357afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_podcast INTEGER," +
1358d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        "album_artist TEXT," +
1359afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio and video
1361afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "duration INTEGER," +
1362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark INTEGER," +
1363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for video
1365afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist TEXT," +
1366afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album TEXT," +
1367afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "resolution TEXT," +
1368afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "tags TEXT," +
1369afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category TEXT," +
1370afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "language TEXT," +
1371afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_data TEXT," +
1372afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for playlists
1374afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "name TEXT," +
1375afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1376afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // media_type is used by the views to emulate the old
1377afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // images, audio_meta, videos and audio_playlist tables.
1378afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "media_type INTEGER," +
1379afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1380afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Value of _id from the old media table.
1381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Used only for updating other tables during database upgrade.
1382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "old_id INTEGER" +
1383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       ");");
1384d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen
1385afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX path_index ON files(_data);");
1386afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1387afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1388afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Copy all data from our obsolete tables to the new files table
138992be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
139092be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Copy audio records first, preserving the _id column.
139192be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We do this to maintain compatibility for content Uris for ringtones.
139292be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Unfortunately we cannot do this for images and videos as well.
139392be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We choose to do this for the audio table because the fragility of Uris
139492be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // for ringtones are the most common problem we need to avoid.
139592be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            db.execSQL("INSERT INTO files (_id," + AUDIO_COLUMNSv99 + ",old_id,media_type)" +
139692be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " SELECT _id," + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO +
139792be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " FROM audio_meta;");
139892be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
1399bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + IMAGE_COLUMNSv407 + ",old_id,media_type) SELECT "
1400bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + IMAGE_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;");
1401bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + VIDEO_COLUMNSv407 + ",old_id,media_type) SELECT "
1402bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + VIDEO_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;");
140316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            if (!internal) {
1404afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT "
1405afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST
1406afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + " FROM audio_playlists;");
1407afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
140816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1409afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Delete the old tables
1410afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS images");
1411afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_meta");
1412afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS video");
1413afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
141416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1415afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create views to replace our old tables
1416bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNSv407 +
1417afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1418afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1419d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 +
1420d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1421d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1422bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNSv407 +
1423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1424afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1425afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1426afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS +
1427afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_PLAYLIST + ";");
142916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            }
143036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
14319491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // create temporary index to make the updates go faster
14329491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("CREATE INDEX tmp ON files(old_id);");
14339491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1434afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update the image_id column in the thumbnails table.
1435afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files "
1436afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = thumbnails.image_id AND files.media_type = "
1437afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ");");
143836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1439afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1440d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // update audio_id in the audio_genres_map table, and
1441d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // audio_playlists_map tables and playlist_id in the audio_playlists_map table
1442afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files "
1443afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = "
1444afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_AUDIO + ");");
1445afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files "
1446afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_playlists_map.audio_id "
1447afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");");
1448d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET playlist_id = (SELECT _id FROM files "
1449d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "WHERE files.old_id = audio_playlists_map.playlist_id "
1450d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + ");");
1451afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
1452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update video_id in the videothumbnails table.
1454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files "
1455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = "
1456afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ");");
1457afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
14589491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // we don't need this index anymore now
14599491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("DROP INDEX tmp;");
14609491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1461afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update indices to work on the files table
1462afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS title_idx");
1463afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS album_id_idx");
1464afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS image_bucket_index");
1465afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS video_bucket_index");
1466afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS sort_index");
1467afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS titlekey_index");
1468afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS artist_id_idx");
1469afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX title_idx ON files(title);");
1470afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1471afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);");
1472afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1474afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1475afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1476afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Recreate triggers for our obsolete tables on the new files table
1477afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
1478afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
1479afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
1480afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
1481afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
148236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1483afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " +
1484afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " +
148536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1486afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
1487afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
148836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
148936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1490afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " +
149149dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " +
149236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1493afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
149436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
149536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1496afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1497afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " +
1498afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " +
1499afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1500afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
1501afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
1502afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1503afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1504afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " +
1505afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " +
1506afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1507afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1508afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "SELECT _DELETE_FILE(old._data);" +
1509afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1510afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
151236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "BEGIN " +
1513afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from files where _id=old._id;" +
1514afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_playlists_map where audio_id=old._id;" +
1515afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_genres_map where audio_id=old._id;" +
151636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "END");
151736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood            }
151836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood        }
151936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1520db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (fromVersion < 301) {
1521db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("DROP INDEX IF EXISTS bucket_index");
1522db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)");
1523db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)");
1524db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
1525db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin
152620405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        if (fromVersion < 302) {
152720405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX parent_index ON files(parent);");
152820405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX format_index ON files(format);");
152920405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        }
153020405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood
15312658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        if (fromVersion < 303) {
15322658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // the album disambiguator hash changed, so rescan songs and force
15332658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // albums to be updated. Artists are unaffected.
15342658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("DELETE from albums");
15352658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
15362658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
15372658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        }
15382658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
15394b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 304 && !internal) {
154051d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            // notifies host when files are deleted
154151d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
154251d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "BEGIN " +
154351d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                        "SELECT _OBJECT_REMOVED(old._id);" +
154451d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "END");
154551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
154651d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood        }
154751d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
15484b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 305 && internal) {
15494b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            // version 304 erroneously added this trigger to the internal database
15504b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
15514b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        }
15524b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood
1553fda522dc66b94057f9c6676cb8ba10bc3b13daeaMarco Nelissen        if (fromVersion < 306 && !internal) {
1554efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // The genre list was expanded and genre string parsing was tweaked, so
1555efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // rebuild the genre list
1556efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
1557efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
1558efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
1559efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
1560efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen        }
1561efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen
156265587f9c204abb2d179527dfdca009f4780e9743Ray Chen        if (fromVersion < 307 && !internal) {
156365587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // Force rescan of image entries to update DATE_TAKEN by either GPSTimeStamp or
156465587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // EXIF local time.
156565587f9c204abb2d179527dfdca009f4780e9743Ray Chen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
156665587f9c204abb2d179527dfdca009f4780e9743Ray Chen                    + FileColumns.MEDIA_TYPE_IMAGE + ";");
156765587f9c204abb2d179527dfdca009f4780e9743Ray Chen        }
156865587f9c204abb2d179527dfdca009f4780e9743Ray Chen
15694f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // Database version 401 did not add storage_id to the internal database.
15704f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // We need it there too, so add it in version 402
15714f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        if (fromVersion < 401 || (fromVersion == 401 && internal)) {
15729be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Add column for MTP storage ID
15739be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("ALTER TABLE files ADD COLUMN storage_id INTEGER;");
15749be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Anything in the database before this upgrade step will be in the primary storage
15759be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("UPDATE files SET storage_id=" + MtpStorage.getStorageId(0) + ";");
15769be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
15779be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
157878b2885edc406273d688536b0eadfea006b20662Marco Nelissen        if (fromVersion < 403 && !internal) {
157978b2885edc406273d688536b0eadfea006b20662Marco Nelissen            db.execSQL("CREATE VIEW audio_genres_map_noid AS " +
158078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    "SELECT audio_id,genre_id from audio_genres_map;");
158178b2885edc406273d688536b0eadfea006b20662Marco Nelissen        }
158278b2885edc406273d688536b0eadfea006b20662Marco Nelissen
15839289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        if (fromVersion < 404) {
15849289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // There was a bug that could cause distinct same-named albums to be
15859289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // combined again. Delete albums and force a rescan.
15869289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("DELETE from albums");
15879289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
15889289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
15899289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        }
15909289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen
1591957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        if (fromVersion < 405) {
1592957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            // Add is_drm column.
1593957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("ALTER TABLE files ADD COLUMN is_drm INTEGER;");
1594957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1595957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("DROP VIEW IF EXISTS audio_meta");
1596957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv405 +
1597957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1598957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1599957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1600957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            recreateAudioView(db);
1601957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        }
1602957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1603b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (fromVersion < 407) {
16047ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            // Rescan files in the media database because a new column has been added
1605b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // in table files in version 405 and to recover from problems populating
1606b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // the genre tables
16077ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            db.execSQL("UPDATE files SET date_modified=0;");
16087ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang        }
16097ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang
1610bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        if (fromVersion < 408) {
1611bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Add the width/height columns for images and video
1612bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN width INTEGER;");
1613bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN height INTEGER;");
1614bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1615bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Rescan files to fill the columns
1616bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("UPDATE files SET date_modified=0;");
1617bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1618bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Update images and video views to contain the width/height columns
1619bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS images");
1620bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS video");
1621bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS +
1622bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1623bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1624bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS +
1625bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1626bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1627bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        }
1628bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
16295809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        if (fromVersion < 409 && !internal) {
16305809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // A bug that prevented numeric genres from being parsed was fixed, so
16315809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // rebuild the genre list
16325809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16335809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
16345809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
16355809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
16365809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        }
16375809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen
1638166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        if (fromVersion < 500) {
1639166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1640166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS videothumbnails_cleanup;");
1641166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        }
1642a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        if (fromVersion < 501) {
1643a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1644a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // the images_cleanup trigger would delete the image file and the entry
1645a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // in the thumbnail table, which in turn would trigger thumbnails_cleanup
1646a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // to delete the thumbnail image
1647a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup;");
1648a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup;");
1649a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        }
16505afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        if (fromVersion < 502) {
16515afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
16525afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup;");
16535afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        }
16545118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        if (fromVersion < 503) {
16555118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            // genre and playlist cleanup now done in mediaprovider code, instead of in a trigger
16565118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
16575118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
16585118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        }
165990c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (fromVersion < 504) {
166090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            // add an index to help with case-insensitive matching of paths
166190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            db.execSQL(
166290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    "CREATE INDEX IF NOT EXISTS path_index_lower ON files(_data COLLATE NOCASE);");
166390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
16647074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen        if (fromVersion < 505) {
16657074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            // Starting with schema 505 we fill in the width/height/resolution columns for videos,
16667074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            // so force a rescan of videos to fill in the blanks
16677074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16687074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen                    + FileColumns.MEDIA_TYPE_VIDEO + ";");
16697074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen        }
167059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen        if (fromVersion < 506) {
167159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // sd card storage got moved to /storage/sdcard0
167259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // first delete everything that already got scanned in /storage before this
167359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // update step was added
167459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
167559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM files WHERE _data LIKE '/storage/%';");
167659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data LIKE '/storage/%';");
167759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data LIKE '/storage/%';");
167859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data LIKE '/storage/%';");
167959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // then rename everything from /mnt/sdcard/ to /storage/sdcard0,
168059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // and from /mnt/external1 to /storage/sdcard1
168159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE files SET " +
168259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
168359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE files SET " +
168459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
168559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE album_art SET " +
168659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
168759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE album_art SET " +
168859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
168959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE thumbnails SET " +
169059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
169159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE thumbnails SET " +
169259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
169359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE videothumbnails SET " +
169459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
169559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE videothumbnails SET " +
169659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
169759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen
169859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            if (!internal) {
169959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
170059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                    "BEGIN " +
170159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                        "SELECT _OBJECT_REMOVED(old._id);" +
170259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                    "END");
170359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            }
170459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen        }
1705a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin        if (fromVersion < 507) {
1706a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin            // we update _data in version 506, we need to update the bucket_id as well
1707677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            updateBucketNames(db);
1708a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin        }
170946e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen        if (fromVersion < 508 && !internal) {
171046e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            // ensure we don't get duplicate entries in the genre map
171146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map_tmp (" +
171246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "_id INTEGER PRIMARY KEY," +
171346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "audio_id INTEGER NOT NULL," +
171446e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "genre_id INTEGER NOT NULL," +
171546e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "UNIQUE (audio_id,genre_id) ON CONFLICT IGNORE" +
171646e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    ");");
171746e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("INSERT INTO audio_genres_map_tmp (audio_id,genre_id)" +
171846e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    " SELECT DISTINCT audio_id,genre_id FROM audio_genres_map;");
171946e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("DROP TABLE audio_genres_map;");
172046e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("ALTER TABLE audio_genres_map_tmp RENAME TO audio_genres_map;");
172146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen        }
1722988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1723988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        if (fromVersion < 509) {
1724988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS log (time DATETIME PRIMARY KEY, message TEXT);");
1725988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        }
1726acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sanityCheck(db, fromVersion);
1727988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000;
1728988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion
1729988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                + " in " + elapsedSeconds + " seconds");
1730988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    }
1731988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1732988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    /**
1733988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen     * Write a persistent diagnostic message to the log table.
1734988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen     */
1735988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    static void logToDb(SQLiteDatabase db, String message) {
1736988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        db.execSQL("INSERT INTO log (time, message) VALUES (datetime('now'),?);",
1737988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                new String[] { message });
1738988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        // delete all but the last 500 rows
1739988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        db.execSQL("DELETE FROM log WHERE rowid IN" +
1740988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                " (SELECT rowid FROM log ORDER BY time DESC LIMIT 500,-1);");
17411d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    }
17421d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen
17431d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    /**
1744216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * Perform a simple sanity check on the database. Currently this tests
1745216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * whether all the _data entries in audio_meta are unique
1746216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     */
1747216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen    private static void sanityCheck(SQLiteDatabase db, int fromVersion) {
1748216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c1 = db.query("audio_meta", new String[] {"count(*)"},
1749216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1750216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"},
1751216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1752216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.moveToFirst();
1753216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.moveToFirst();
1754216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num1 = c1.getInt(0);
1755216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num2 = c2.getInt(0);
1756216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.close();
1757216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.close();
1758216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        if (num1 != num2) {
1759216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            Log.e(TAG, "audio_meta._data column is not unique while upgrading" +
1760216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                    " from schema " +fromVersion + " : " + num1 +"/" + num2);
1761216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            // Delete all audio_meta rows so they will be rebuilt by the media scanner
1762216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("DELETE FROM audio_meta;");
1763216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        }
1764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
1767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
1768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
1769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
1770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
1771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
1772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
177395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1775677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen     * Update the bucket_id and bucket_display_name columns for images and videos
1776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1779677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen    private static void updateBucketNames(SQLiteDatabase db) {
1780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
1781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
1784677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            // update only images and videos
1785677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            Cursor cursor = db.query("files", columns, "media_type=1 OR media_type=3",
1786677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen                    null, null, null, null);
1787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
17909491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                String [] rowId = new String[1];
1791988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                ContentValues values = new ContentValues();
1792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
1794988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    rowId[0] = cursor.getString(idColumnIndex);
1795d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    if (data != null) {
1796988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        values.clear();
1797d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        computeBucketValues(data, values);
1798677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen                        db.update("files", values, "_id=?", rowId);
1799d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    } else {
1800d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        Log.w(TAG, "null data at id " + rowId);
1801d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    }
1802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
1814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
1815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
1819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
1820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
1823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
1827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
1828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
1829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
1831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
1832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
1833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
1834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
1835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
1836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
1837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1847988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
1851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
1854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
1855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
1856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
1857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
1860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
1861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
1862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
1863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
1864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
1866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
1867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
1868d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood        values.put(ImageColumns.BUCKET_ID, path.hashCode());
1869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
1870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
1875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
1878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
1879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
1880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
1881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
1882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
1884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1886b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
1887498b62c2912302a23532c73a028a7684c5df33caRay Chen     * Copy taken time from date_modified if we lost the original value (e.g. after factory reset)
1888498b62c2912302a23532c73a028a7684c5df33caRay Chen     * This works for both video and image tables.
1889b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     *
1890b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     * @param values the content values, where taken time is updated.
1891b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     */
1892b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    private static void computeTakenTime(ContentValues values) {
1893b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        if (! values.containsKey(Images.Media.DATE_TAKEN)) {
1894b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // This only happens when MediaScanner finds an image file that doesn't have any useful
1895b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // reference to get this value. (e.g. GPSTimeStamp)
1896498b62c2912302a23532c73a028a7684c5df33caRay Chen            Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED);
1897b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            if (lastModified != null) {
1898b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                values.put(Images.Media.DATE_TAKEN, lastModified * 1000);
1899b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            }
1900b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        }
1901b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    }
1902b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen
1903b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    /**
1904b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
1905b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
1906b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
1907b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
1908b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
1909b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
1910b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
1911b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
1912b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c == null) return false;
1913b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1914b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
1915b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1916b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c.moveToFirst()) {
1917e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen            long id = c.getLong(0);
1918b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String path = c.getString(1);
1919b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            long magic = c.getLong(2);
1920b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
19219299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            MediaThumbRequest req = requestMediaThumbnail(path, origUri,
19229299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    MediaThumbRequest.PRIORITY_HIGH, magic);
19239299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            if (req == null) {
19249299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                return false;
19259299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            }
19269299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            synchronized (req) {
19279299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                try {
19289299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    while (req.mState == MediaThumbRequest.State.WAIT) {
19299299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        req.wait();
193020434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
19319299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                } catch (InterruptedException e) {
19329299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    Log.w(TAG, e);
19339299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                }
19349299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                if (req.mState == MediaThumbRequest.State.DONE) {
19359299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    result = true;
1936b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1937b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
1938b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1939b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        c.close();
1940b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1941b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
1942b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1943b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1944e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid,
1945e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo) {
1946e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllOrigId = (id == -1);
1947e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllGroupId = (gid == -1);
1948e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        return (req.mCallingPid == pid) &&
1949e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllGroupId || req.mGroupId == gid) &&
1950e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllOrigId || req.mOrigId == id) &&
1951e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (req.mIsVideo == isVideo);
1952e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    }
1953e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
1954b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
1955b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
1956b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
1957b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
1958b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
1959b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
1960b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
1961b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
1962b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1963b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1964b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
1965b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
1966b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
1967b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
1968b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1969b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1970b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1971b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
197220434e032e498b716f87cce2f23dd646819218bfRay Chen        boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel"));
1973e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        Uri origUri = uri.buildUpon().encodedPath(
1974e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                uri.getPath().replaceFirst("thumbnails", "media"))
1975e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                .appendPath(origId).build();
1976b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1977b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
197820434e032e498b716f87cce2f23dd646819218bfRay Chen            Log.w(TAG, "original media doesn't exist or it's canceled.");
1979b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
198020434e032e498b716f87cce2f23dd646819218bfRay Chen        } else if (cancelRequest) {
1981e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            String groupId = uri.getQueryParameter("group_id");
1982e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo = "video".equals(uri.getPathSegments().get(1));
198320434e032e498b716f87cce2f23dd646819218bfRay Chen            int pid = Binder.getCallingPid();
198420434e032e498b716f87cce2f23dd646819218bfRay Chen            long id = -1;
1985e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            long gid = -1;
1986e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
198720434e032e498b716f87cce2f23dd646819218bfRay Chen            try {
198820434e032e498b716f87cce2f23dd646819218bfRay Chen                id = Long.parseLong(origId);
1989e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                gid = Long.parseLong(groupId);
199020434e032e498b716f87cce2f23dd646819218bfRay Chen            } catch (NumberFormatException ex) {
199120434e032e498b716f87cce2f23dd646819218bfRay Chen                // invalid cancel request
199220434e032e498b716f87cce2f23dd646819218bfRay Chen                return false;
199320434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1994e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
199520434e032e498b716f87cce2f23dd646819218bfRay Chen            synchronized (mMediaThumbQueue) {
1996e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                if (mCurrentThumbRequest != null &&
1997e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                        matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) {
199820434e032e498b716f87cce2f23dd646819218bfRay Chen                    synchronized (mCurrentThumbRequest) {
199920434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL;
200020434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.notifyAll();
200120434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
200220434e032e498b716f87cce2f23dd646819218bfRay Chen                }
200320434e032e498b716f87cce2f23dd646819218bfRay Chen                for (MediaThumbRequest mtq : mMediaThumbQueue) {
2004e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                    if (matchThumbRequest(mtq, pid, id, gid, isVideo)) {
200520434e032e498b716f87cce2f23dd646819218bfRay Chen                        synchronized (mtq) {
200620434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.mState = MediaThumbRequest.State.CANCEL;
200720434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.notifyAll();
200820434e032e498b716f87cce2f23dd646819218bfRay Chen                        }
200920434e032e498b716f87cce2f23dd646819218bfRay Chen
201020434e032e498b716f87cce2f23dd646819218bfRay Chen                        mMediaThumbQueue.remove(mtq);
201120434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
201220434e032e498b716f87cce2f23dd646819218bfRay Chen                }
201320434e032e498b716f87cce2f23dd646819218bfRay Chen            }
2014b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2015b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2016b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
2017b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
2018b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2019b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
2020b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
2021b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
2022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
2024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
2025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
2026baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        List<String> prependArgs = new ArrayList<String>();
2027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
202801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
2029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
2030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
2031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
2032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
2033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
20350027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
20360027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                c.addRow(new String[] {mMediaScannerVolume});
20370027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                return c;
2038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
20410027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // Used temporarily (until we have unique media IDs) to get an identifier
20420027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // for the current sd card, so that the music app doesn't have to use the
20430027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // non-public getFatVolumeId method
20440027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        if (table == FS_ID) {
20450027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"fsid"});
20460027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            c.addRow(new Integer[] {mVolumeId});
20470027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            return c;
20480027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        }
20490027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
2050704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        if (table == VERSION) {
2051704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"version"});
205290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            c.addRow(new Integer[] {getDatabaseVersion(getContext())});
2053704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            return c;
2054704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        }
2055704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen
2056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
205710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
205810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
2060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
206110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
206210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getReadableDatabase();
20635fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) return null;
2064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
20654574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
2066c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String filter = uri.getQueryParameter("filter");
2067c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String [] keywords = null;
2068c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        if (filter != null) {
2069c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            filter = Uri.decode(filter).trim();
2070c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            if (!TextUtils.isEmpty(filter)) {
2071c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                String [] searchWords = filter.split(" ");
2072c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                keywords = new String[searchWords.length];
2073c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                Collator col = Collator.getInstance();
2074c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                col.setStrength(Collator.PRIMARY);
2075c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; i < searchWords.length; i++) {
2076c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    String key = MediaStore.Audio.keyFor(searchWords[i]);
2077c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("\\", "\\\\");
2078c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("%", "\\%");
2079c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("_", "\\_");
2080c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    keywords[i] = key;
2081c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2082c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            }
2083c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        }
2084db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (uri.getQueryParameter("distinct") != null) {
2085db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            qb.setDistinct(true);
2086db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
2087c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen
2088b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
2089702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
2091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2093702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2107baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2108baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2112b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2113b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
2114b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
2115b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2116b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2120d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2121ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.equalsIgnoreCase("is_music=1")
2122ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                          || selection.equalsIgnoreCase("is_podcast=1") )
2123c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2124c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2125ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting songs");
2126ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2127ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2128ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio");
2129c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2130c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2131c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2132c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2133c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2134c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2135baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'");
2136baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2137c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2138ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
2143baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2144baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
2150baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_genres_map WHERE audio_id=?)");
2151baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2156baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2157baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
2163baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_playlists_map WHERE audio_id=?)");
2164baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2169baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2170baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2179baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2180baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2183bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen            case AUDIO_GENRES_ALL_MEMBERS:
2184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
218578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                {
218678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // if simpleQuery is true, we can do a simpler query on just audio_genres_map
218778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // we can do this if we have no keywords and our projection includes just columns
218878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // from audio_genres_map
218978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    boolean simpleQuery = (keywords == null && projectionIn != null
219078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            && (selection == null || selection.equalsIgnoreCase("genre_id=?")));
219178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (projectionIn != null) {
219278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; i < projectionIn.length; i++) {
219378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            String p = projectionIn[i];
219478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (p.equals("_id")) {
219578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // note, this is different from playlist below, because
219678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // "_id" used to (wrongly) be the audio id in this query, not
219778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // the row id of the entry in the map, and we preserve this
219878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // behavior for backwards compatibility
219978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
220078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
220178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (simpleQuery && !(p.equals("audio_id") ||
220278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    p.equals("genre_id"))) {
220378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
220478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
220578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
220678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
220778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (simpleQuery) {
220878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid");
2209bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2210baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere("genre_id=?");
2211baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2212bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
221378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    } else {
221478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid, audio");
2215bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        qb.appendWhere("audio._id = audio_id");
2216bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2217baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere(" AND genre_id=?");
2218baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2219bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
222078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; keywords != null && i < keywords.length; i++) {
222178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(" AND ");
222278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
222378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.ALBUM_KEY +
222478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.TITLE_KEY +
2225baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                    " LIKE ? ESCAPE '\\'");
2226baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add("%" + keywords[i] + "%");
222778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
222878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
222978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                }
2230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2238baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2239baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2242b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2244e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // if simpleQuery is true, we can do a simpler query on just audio_playlists_map
2245e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // we can do this if we have no keywords and our projection includes just columns
2246e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // from audio_playlists_map
22474382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                boolean simpleQuery = (keywords == null && projectionIn != null
22484382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                        && (selection == null || selection.equalsIgnoreCase("playlist_id=?")));
224997e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
225097e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
2251e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        String p = projectionIn[i];
2252e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (simpleQuery && !(p.equals("audio_id") ||
2253e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                p.equals("playlist_id") || p.equals("play_order"))) {
2254e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                            simpleQuery = false;
2255e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        }
2256e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (p.equals("_id")) {
225797e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
225897e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
2259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2261e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                if (simpleQuery) {
2262e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map");
2263baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("playlist_id=?");
2264baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2265e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                } else {
2266e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map, audio");
2267baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("audio._id = audio_id AND playlist_id=?");
2268baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2269e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2270e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(" AND ");
2271e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2272e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2273e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.TITLE_KEY +
2274baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2275baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2276e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    }
2277c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2278b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                if (table == AUDIO_PLAYLISTS_ID_MEMBERS_ID) {
2279baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere(" AND audio_playlists_map._id=?");
2280baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(5));
2281b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                }
2282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2289baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2290baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2293b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
2294b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2295b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
2296b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
2297b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2298b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2299b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2300b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
2302d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2303ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2304c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2305c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2306ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting artists");
2307ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2308ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct artist_id)";
2309ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2310ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2311ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("artist_info");
2312c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2313c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2314c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2315c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2316c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2317baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2318baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2319c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2320ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
2324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
2325baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2326baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
2330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
2331acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
2332acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        " audio.album_id=album_art.album_id");
2333acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
2334baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "artists_albums_map WHERE artist_id=?)");
2335baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(aid);
2336c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; keywords != null && i < keywords.length; i++) {
2337c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(" AND ");
2338c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2339c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            "||" + MediaStore.Audio.Media.ALBUM_KEY +
2340baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            " LIKE ? ESCAPE '\\'");
2341baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add("%" + keywords[i] + "%");
2342c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2343acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                groupBy = "audio.album_id";
2344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
2345acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
2346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
2347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
2348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
2351d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2352ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2353c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2354c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2355ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting albums");
2356ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2357ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct album_id)";
2358ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2359ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2360ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("album_info");
2361c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2362c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2363c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2364c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2365c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2366c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2367baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2368baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2369c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2370ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
2374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
2375baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2376baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
2380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
2381baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("album_id=?");
2382baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2385a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
2386a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
2387a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
2388a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
2389a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
2390baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection,
2391baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        combine(prependArgs, selectionArgs), sort, table, limit);
2392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
239316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
2394e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
2395baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2396baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(2));
2397b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                // fall through
239816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
2399e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
240016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                qb.setTables("files");
2401b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                break;
2402b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2403e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            case MTP_OBJECT_REFERENCES:
2404e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int handle = Integer.parseInt(uri.getPathSegments().get(2));
240510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                return getObjectReferences(helper, db, handle);
2406e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
2408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
2409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2411baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection,
2412baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        //        combine(prependArgs, selectionArgs), groupBy, null, sort, limit));
2413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
2414baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
2415b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
2417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            c.setNotificationUri(getContext().getContentResolver(), uri);
2418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2419b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
2421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2423baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    private String[] combine(List<String> prepend, String[] userArgs) {
2424baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int presize = prepend.size();
2425baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        if (presize == 0) {
2426baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            return userArgs;
2427baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2428baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2429baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int usersize = (userArgs != null) ? userArgs.length : 0;
2430baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        String [] combined = new String[presize + usersize];
2431baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < presize; i++) {
2432baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[i] = prepend.get(i);
2433baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2434baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < usersize; i++) {
2435baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[presize + i] = userArgs[i];
2436baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2437baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        return combined;
2438baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    }
2439baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
2441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
24424574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
24434574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
2444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
244518c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen        String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment();
2446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
2447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
2449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
2450a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
2451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Collator col = Collator.getInstance();
2452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        col.setStrength(Collator.PRIMARY);
2453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
2454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
2455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
2456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
24573001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            String key = MediaStore.Audio.keyFor(searchWords[i]);
24583001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("\\", "\\\\");
24593001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("%", "\\%");
24603001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("_", "\\_");
2461a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
2462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
24633001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                        searchWords[i].equals("the")) ? "%" : "%" + key + "%";
2464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2466a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
2467a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
2468a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
24693001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where = "match LIKE ? ESCAPE '\\'";
2470a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
24713001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where += " AND match LIKE ? ESCAPE '\\'";
2472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2475a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
2476a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
2477a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
2478a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
2479a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
2480a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
2481a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
2482a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
2483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
24844574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
2485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
2489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
2491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2495c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case FILES_ID:
249626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                Cursor c = null;
249726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                try {
249826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    c = query(url, MIME_TYPE_PROJECTION, null, null, null);
249926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null && c.getCount() == 1) {
250026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.moveToFirst();
250126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        String mimeType = c.getString(1);
250226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.deactivate();
250326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        return mimeType;
250426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
250526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                } finally {
250626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null) {
250726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.close();
250826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
2509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
2514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
2515804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen            case AUDIO_ALBUMART_ID:
2516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
2518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
2521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
2523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
2527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
2530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
2533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
2536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
2539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2540804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen        throw new IllegalStateException("Unknown URL : " + url);
2541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
2545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * present a new file is created.
2546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
2548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
2549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
2551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
2552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
2553801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        String file = initialValues.getAsString(MediaStore.MediaColumns.DATA);
2554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
2555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
2556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
2557801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            values.put(MediaStore.MediaColumns.DATA, file);
2558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
2560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!ensureFileExists(file)) {
2563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalStateException("Unable to create new file: " + file);
2564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
2566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2568d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectAdded(long objectHandle) {
256934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
257034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
257134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
257234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectAdded((int)objectHandle);
257334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
257434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectAdded", e);
257534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
257634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2577d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2578d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2579d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2580d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2581d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectRemoved(long objectHandle) {
258234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
258334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
258434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
258534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectRemoved((int)objectHandle);
258634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
258734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectRemoved", e);
258834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
258934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2590d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2591d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2592d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2593d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
2596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
2598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
2599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
260010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
260110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
260510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
26065fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) {
26075fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            throw new IllegalStateException("Couldn't open database for " + uri);
26085fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        }
2609ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2610ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
2611ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            return playlistBulkInsert(db, uri, values);
2612e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } else if (match == MTP_OBJECT_REFERENCES) {
2613e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            int handle = Integer.parseInt(uri.getPathSegments().get(2));
261410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            return setObjectReferences(helper, db, handle, values);
2615ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2616ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
26172dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
26192dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
2620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
2621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
2623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
2624801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                if (values[i] != null) {
26252dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                    insertInternal(uri, match, values[i], notifyRowIds);
2626801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                }
2627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
2632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
26332dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
26342dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        // Notify MTP (outside of successful transaction)
26352dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        notifyMtp(notifyRowIds);
26362dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
2638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
2639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2642801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood    public Uri insert(Uri uri, ContentValues initialValues) {
26435d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        int match = URI_MATCHER.match(uri);
26442dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
26452dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
26462dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
26472dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        notifyMtp(notifyRowIds);
26482dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
26495d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // do not signal notification for MTP objects.
26505d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // we will signal instead after file transfer is successful.
2651e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        if (newUri != null && match != MTP_OBJECTS) {
2652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
2653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
26572dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    private void notifyMtp(ArrayList<Long> rowIds) {
26582dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        int size = rowIds.size();
26592dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        for (int i = 0; i < size; i++) {
26602dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke            sendObjectAdded(rowIds.get(i).longValue());
26612dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        }
26622dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    }
26632dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2664ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) {
2665ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        DatabaseUtils.InsertHelper helper =
2666ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            new DatabaseUtils.InsertHelper(db, "audio_playlists_map");
26678b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
26688b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID);
26698b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
26708b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        long playlistId = Long.parseLong(uri.getPathSegments().get(3));
2671ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2672ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        db.beginTransaction();
2673ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        int numInserted = 0;
2674ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        try {
2675ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            int len = values.length;
2676ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            for (int i = 0; i < len; i++) {
26778b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.prepareForInsert();
26788b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // getting the raw Object and converting it long ourselves saves
26798b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // an allocation (the alternative is ContentValues.getAsLong, which
26808b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // returns a Long object)
26818b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                long audioid = ((Number) values[i].get(
26828b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue();
26838b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(audioidcolidx, audioid);
26848b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playlistididx, playlistId);
26858b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // convert to int ourselves to save an allocation.
26868b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                int playorder = ((Number) values[i].get(
26878b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue();
26888b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playorderidx, playorder);
26898b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.execute();
2690ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            }
2691ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            numInserted = len;
2692ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.setTransactionSuccessful();
2693ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        } finally {
2694ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.endTransaction();
2695ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            helper.close();
2696ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2697ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        getContext().getContentResolver().notifyChange(uri, null);
2698ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        return numInserted;
2699ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    }
2700ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
270110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertDirectory(DatabaseHelper helper, SQLiteDatabase db, String path) {
270210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (LOCAL_LOGV) Log.v(TAG, "inserting directory " + path);
2703ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        ContentValues values = new ContentValues();
2704ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION);
2705ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.DATA, path);
270610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        values.put(FileColumns.PARENT, getParent(helper, db, path));
27079be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        values.put(FileColumns.STORAGE_ID, getStorageId(path));
2708f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        File file = new File(path);
2709f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        if (file.exists()) {
2710f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
2711f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        }
271210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumInserts++;
2713ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2714ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        sendObjectAdded(rowId);
2715ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        return rowId;
2716ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
2717ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
271810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getParent(DatabaseHelper helper, SQLiteDatabase db, String path) {
2719b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        int lastSlash = path.lastIndexOf('/');
2720b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (lastSlash > 0) {
2721b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String parentPath = path.substring(0, lastSlash);
27229be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            for (int i = 0; i < mExternalStoragePaths.length; i++) {
27239be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (parentPath.equals(mExternalStoragePaths[i])) {
27249be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return 0;
27259be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
2726b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
27277f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Long cid = mDirectoryCache.get(parentPath);
27287f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            if (cid != null) {
27297f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath);
27307f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return cid;
27317f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            }
27327f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
2733f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood            // Use "LIKE" instead of "=" on case insensitive file systems so we do a
2734f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood            // case insensitive match when looking for parent directory.
27357f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: investigate whether a "nocase" constraint on the column and
27367f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // using "=" would give the same result faster.
27375461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            String selection = (mCaseInsensitivePaths ? MediaStore.MediaColumns.DATA + " LIKE ?1"
27385461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    // The like above makes it use the index.
27395461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    // The comparison below makes it correct when the path has wildcard chars
27405461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    + " AND lower(_data)=lower(?1)"
2741a9bb89771934314157dd26253195dc16bddc2cfaDongwon Kang                    // search only directories.
274210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    + " AND format=" + MtpConstants.FORMAT_ASSOCIATION
2743f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood                    : MediaStore.MediaColumns.DATA + "=?");
2744b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String [] selargs = { parentPath };
274510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
27467f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
2747b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            try {
27487f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                long id;
2749b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c == null || c.getCount() == 0) {
2750b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    // parent isn't in the database - so add it
275110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    id = insertDirectory(helper, db, parentPath);
27527f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath);
2753b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                } else {
27545461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    if (c.getCount() > 1) {
27555461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        Log.e(TAG, "more than one match for " + parentPath);
27565461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    }
2757b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    c.moveToFirst();
27587f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    id = c.getLong(0);
27597f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath);
2760b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                }
27617f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.put(parentPath, id);
27627f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return id;
2763b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            } finally {
2764b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c != null) c.close();
2765b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
2766b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        } else {
2767b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            return 0;
2768b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        }
2769b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    }
2770b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
27719be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private int getStorageId(String path) {
27729be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        for (int i = 0; i < mExternalStoragePaths.length; i++) {
27739be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String test = mExternalStoragePaths[i];
27749be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (path.startsWith(test)) {
27759be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int length = test.length();
27769be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (path.length() == length || path.charAt(length) == '/') {
27779be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return MtpStorage.getStorageId(i);
27789be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
27799be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
27809be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
27819be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        // default to primary storage
27829be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        return MtpStorage.getStorageId(0);
27839be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    }
27849be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
278510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, int mediaType,
27862dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                            boolean notify, ArrayList<Long> notifyRowIds) {
278710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
2788afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        ContentValues values = null;
2789afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2790afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        switch (mediaType) {
2791afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_IMAGE: {
279210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".jpg", "DCIM/Camera");
2793afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2794afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2795afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String data = values.getAsString(MediaColumns.DATA);
2796afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
2797afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    computeDisplayName(data, values);
2798afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2799afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2800afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2801afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2802afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2803afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_AUDIO: {
2804afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // SQLite Views are read-only, so we need to deconstruct this
2805afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // insert and do inserts into the underlying tables.
2806afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // If doing this here turns out to be a performance bottleneck,
2807afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // consider moving this to native code and using triggers on
2808afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the view.
2809afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values = new ContentValues(initialValues);
2810afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
28112658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
28122658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
28132658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                values.remove(MediaStore.Audio.Media.COMPILATION);
28142658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
2815afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Insert the artist into the artist table and remove it from
2816afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the input values
2817afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                Object so = values.get("artist");
2818afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String s = (so == null ? "" : so.toString());
2819afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("artist");
2820afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long artistRowId;
282110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> artistCache = helper.mArtistCache;
2822801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String path = values.getAsString(MediaStore.MediaColumns.DATA);
2823afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(artistCache) {
2824afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = artistCache.get(s);
2825afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
282610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        artistRowId = getKeyIdForName(helper, db,
282710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "artists", "artist_key", "artist",
2828afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, s, path, 0, null, artistCache, uri);
2829afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
2830afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        artistRowId = temp.longValue();
2831afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
2832afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2833afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String artist = s;
2834afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2835afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Do the same for the album field
2836afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.get("album");
2837afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
2838afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("album");
2839afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long albumRowId;
284010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> albumCache = helper.mAlbumCache;
2841afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(albumCache) {
28422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    int albumhash = 0;
28432658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    if (albumartist != null) {
28442658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = albumartist.hashCode();
28452658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else if (compilation != null && compilation.equals("1")) {
28462658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        // nothing to do, hash already set
28472658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else {
28482658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
28492658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    }
2850afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    String cacheName = s + albumhash;
2851afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = albumCache.get(cacheName);
2852afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
285310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        albumRowId = getKeyIdForName(helper, db,
285410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "albums", "album_key", "album",
2855afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, cacheName, path, albumhash, artist, albumCache, uri);
2856afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
2857afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        albumRowId = temp;
2858afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
2859afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
28605d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
2861afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("artist_id", Integer.toString((int)artistRowId));
2862afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("album_id", Integer.toString((int)albumRowId));
2863afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.getAsString("title");
2864afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
2865afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title_key", MediaStore.Audio.keyFor(s));
2866afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // do a final trim of the title, in case it started with the special
2867afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // "sort first" character (ascii \001)
2868afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("title");
2869afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title", s.trim());
2870b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2871801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                computeDisplayName(values.getAsString(MediaStore.MediaColumns.DATA), values);
2872afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2873afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2874afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2875afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_VIDEO: {
287610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".3gp", "video");
2877801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String data = values.getAsString(MediaStore.MediaColumns.DATA);
2878afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeDisplayName(data, values);
2879afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2880afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2881afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2882afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2883afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2884afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (values == null) {
2885afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(initialValues);
2886afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2887fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // compute bucket_id and bucket_display_name for all files
2888afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String path = values.getAsString(MediaStore.MediaColumns.DATA);
2889fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        if (path != null) {
2890fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            computeBucketValues(path, values);
2891fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        }
2892fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2893afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2894afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        long rowId = 0;
2895afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer i = values.getAsInteger(
2896afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
2897afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (i != null) {
2898afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = i.intValue();
2899afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(values);
2900afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
2901afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2902afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2903afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String title = values.getAsString(MediaStore.MediaColumns.TITLE);
29043572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen        if (title == null && path != null) {
2905c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            title = MediaFile.getFileTitle(path);
2906c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2907c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        values.put(FileColumns.TITLE, title);
2908c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2909afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE);
2910afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer formatObject = values.getAsInteger(FileColumns.FORMAT);
2911c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        int format = (formatObject == null ? 0 : formatObject.intValue());
2912c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format == 0) {
291363b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang            if (TextUtils.isEmpty(path)) {
2914c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                // special case device created playlists
2915afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
2916c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST);
2917c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    // create a file path for the benefit of MTP
29189be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    path = mExternalStoragePaths[0]
2919afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            + "/Playlists/" + values.getAsString(Audio.Playlists.NAME);
2920c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(MediaStore.MediaColumns.DATA, path);
292110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    values.put(FileColumns.PARENT, getParent(helper, db, path));
2922c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                } else {
292363b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang                    Log.e(TAG, "path is empty in insertFile()");
2924c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                }
2925c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            } else {
2926c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                format = MediaFile.getFormatCode(path, mimeType);
2927c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
2928c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2929c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format != 0) {
2930c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.FORMAT, format);
2931c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            if (mimeType == null) {
2932c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                mimeType = MediaFile.getMimeTypeForFormatCode(format);
2933c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
2934c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2935c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2936801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        if (mimeType == null && path != null) {
2937c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            mimeType = MediaFile.getMimeTypeForFile(path);
2938c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2939c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (mimeType != null) {
2940c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.MIME_TYPE, mimeType);
2941afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2942f1f6a9e343033de33fc748f659b9221f8d5b06e1Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_NONE && !MediaScanner.isNoMediaPath(path)) {
2943afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int fileType = MediaFile.getFileTypeForMimeType(mimeType);
2944afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (MediaFile.isAudioFileType(fileType)) {
2945afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_AUDIO;
2946afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isVideoFileType(fileType)) {
2947afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_VIDEO;
2948afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isImageFileType(fileType)) {
2949afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_IMAGE;
2950afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isPlayListFileType(fileType)) {
2951afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
2952afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2953afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2954c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2955afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        values.put(FileColumns.MEDIA_TYPE, mediaType);
2956c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2957afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (rowId == 0) {
2958afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
29593572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen                String name = values.getAsString(Audio.Playlists.NAME);
2960282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                if (name == null && path == null) {
2961282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                    // MediaScanner will compute the name from the path if we have one
2962afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
2963282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                            "no name was provided when inserting abstract playlist");
2964afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2965afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            } else {
2966a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu                if (path == null) {
2967afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // path might be null for playlists created on the device
2968afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // or transfered via MTP
2969afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
2970afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "no path was provided when inserting new file");
2971afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2972e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2973b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2974f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            // make sure modification date and size are set
2975f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            if (path != null) {
2976f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                File file = new File(path);
2977f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                if (file.exists()) {
2978f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                    values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
297916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.SIZE, file.length());
2980e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
29815d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
2982b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2983afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            Long parent = values.getAsLong(FileColumns.PARENT);
29845d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (parent == null) {
2985e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path != null) {
298610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    long parentId = getParent(helper, db, path);
298716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.PARENT, parentId);
2988e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
29899be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
29909be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            Integer storage = values.getAsInteger(FileColumns.STORAGE_ID);
29919be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (storage == null) {
29929be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int storageId = getStorageId(path);
29939be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                values.put(FileColumns.STORAGE_ID, storageId);
29945d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
2995b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
299610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumInserts++;
2997afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2998afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId);
2999afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
300010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (rowId != -1 && notify) {
30012dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                notifyRowIds.add(rowId);
3002afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
30035d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } else {
300410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates++;
300516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.update("files", values, FileColumns._ID + "=?",
3006afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    new String[] { Long.toString(rowId) });
30075d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        }
3008d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen        if (format == MtpConstants.FORMAT_ASSOCIATION) {
3009d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen            mDirectoryCache.put(path, rowId);
3010d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen        }
3011afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3012afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        return rowId;
30131717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    }
30141717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
301510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private Cursor getObjectReferences(DatabaseHelper helper, SQLiteDatabase db, int handle) {
301610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
3017a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
3018e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
3019e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
3020e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
3021e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
3022afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long playlistId = c.getLong(0);
3023afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
3024afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
3025e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
3026e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return null;
3027e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
302810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumQueries++;
3029e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                return db.rawQuery(OBJECT_REFERENCES_QUERY,
3030afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        new String[] { Long.toString(playlistId) } );
3031e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3032e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
3033e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
3034e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
3035e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3036e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3037e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return null;
3038e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
3039e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
304010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int setObjectReferences(DatabaseHelper helper, SQLiteDatabase db,
304110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            int handle, ContentValues values[]) {
3042e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // first look up the media table and media ID for the object
3043e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        long playlistId = 0;
304410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
3045a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
3046e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
3047e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
3048e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
3049e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
3050afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
3051afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
3052e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
3053e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return 0;
3054e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3055afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                playlistId = c.getLong(0);
3056e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3057e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
3058e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
3059e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
3060e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3061e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3062e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (playlistId == 0) {
3063e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            return 0;
3064e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3065e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3066e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // next delete any existing entries
306710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumDeletes++;
306810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        db.delete("audio_playlists_map", "playlist_id=?",
3069e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] { Long.toString(playlistId) });
3070e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3071e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // finally add the new entries
3072e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int count = values.length;
3073e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int added = 0;
3074e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        ContentValues[] valuesList = new ContentValues[count];
3075e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        for (int i = 0; i < count; i++) {
3076e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // convert object ID to audio ID
3077e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long audioId = 0;
3078e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID);
307910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
3080a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            c = db.query("files", sMediaTableColumns, "_id=?",
3081e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    new String[] {  Long.toString(objectId) },
3082e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    null, null, null);
3083e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            try {
3084e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null && c.moveToNext()) {
3085afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    int mediaType = c.getInt(1);
308650d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood                    if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) {
3087e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        // we only allow audio files in playlists, so skip
3088e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        continue;
3089e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    }
3090afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    audioId = c.getLong(0);
3091e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3092e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            } finally {
3093e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null) {
3094e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    c.close();
3095e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3096e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3097e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (audioId != 0) {
3098e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                ContentValues v = new ContentValues();
3099e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId);
3100e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
3101a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added);
3102a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                valuesList[added++] = v;
3103e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3104e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3105e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (added < count) {
3106e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // we weren't able to find everything on the list, so lets resize the array
3107e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // and pass what we have.
3108e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            ContentValues[] newValues = new ContentValues[added];
3109e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            System.arraycopy(valuesList, 0, newValues, 0, added);
3110e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            valuesList = newValues;
3111e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3112e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return playlistBulkInsert(db,
3113e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId),
3114e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                valuesList);
3115e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
3116e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3117b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private static final String[] GENRE_LOOKUP_PROJECTION = new String[] {
3118b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres._ID, // 0
3119b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres.NAME, // 1
3120b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    };
3121b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3122b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private void updateGenre(long rowId, String genre) {
3123b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri uri = null;
3124b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Cursor cursor = null;
3125b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri genresUri = MediaStore.Audio.Genres.getContentUri("external");
3126b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        try {
3127b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // see if the genre already exists
3128b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            cursor = query(genresUri, GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?",
3129b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            new String[] { genre }, null);
3130b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor == null || cursor.getCount() == 0) {
3131b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre does not exist, so create the genre in the genre table
3132b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                ContentValues values = new ContentValues();
3133b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                values.put(MediaStore.Audio.Genres.NAME, genre);
3134b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = insert(genresUri, values);
3135b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            } else {
3136b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre already exists, so compute its Uri
3137b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.moveToNext();
3138b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = ContentUris.withAppendedId(genresUri, cursor.getLong(0));
3139b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3140b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (uri != null) {
3141b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = Uri.withAppendedPath(uri, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY);
3142b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3143b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        } finally {
3144b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // release the cursor if it exists
3145b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor != null) {
3146b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.close();
3147b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3148b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3149b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3150b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (uri != null) {
3151b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // add entry to audio_genre_map
3152b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            ContentValues values = new ContentValues();
3153b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
3154b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            insert(uri, values);
3155b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3156b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    }
3157b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
31582dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
31592dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                               ArrayList<Long> notifyRowIds) {
3160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
3161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3162d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
3163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3165bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
316610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
316710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
316810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
316910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
317010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
317110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStartTime = SystemClock.currentTimeMicro();
317210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
3174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3176b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
317738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String path = null;
3178b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3179b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3180b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
318138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            path = initialValues.getAsString(MediaStore.MediaColumns.DATA);
3182b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3183b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
318438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
318610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
318710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null && match != VOLUMES && match != MTP_CONNECTED) {
3188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
319110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
3192819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null
319310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                : helper.getWritableDatabase());
3194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
3196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
31972dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
31982dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds);
3199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
3201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
3202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3206b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
3207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
320810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3209b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
321010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
3212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
3214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContentUri(uri.getPathSegments().get(0)), rowId);
3215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3219b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
3220b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
322110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3222b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
322310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3224b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
3225b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
3226b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
3227b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            getContentUri(uri.getPathSegments().get(0)), rowId);
3228b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
3229b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3230b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
3231b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
32332dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
32342dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_AUDIO, true, notifyRowIds);
3235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
3237b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    if (genre != null) {
3238b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        updateGenre(rowId, genre);
3239b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    }
3240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
3245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3246bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
324810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3249ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_genres_map", "genre_id", values);
3250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
3257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3258bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
326010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
3262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
3263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
327010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3271bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                rowId = db.insert("audio_genres", "audio_id", initialValues);
3272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
3274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
3279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
3280bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
328210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
3284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
3291bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
32932dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, values,
32942dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_PLAYLIST, true, notifyRowIds);
3295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
3297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
3303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
3304bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
330610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3307ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_playlists_map", "playlist_id", values);
3308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
33152dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
33162dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds);
3317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3318b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
3319b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            uri.getPathSegments().get(0)), rowId);
3320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3324c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case AUDIO_ALBUMART: {
332510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                if (helper.mInternal) {
3326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
3327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3328bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = null;
3329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
3330bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
3331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
3332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
3333bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = initialValues;
3334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
333510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3336801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
3337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3341c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
33445619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            {
33455619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                String name = initialValues.getAsString("name");
33465619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                Uri attachedVolume = attachVolume(name);
33475619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) {
33485619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    DatabaseHelper dbhelper = getDatabaseForUri(attachedVolume);
33495619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    if (dbhelper == null) {
33505619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                        Log.e(TAG, "no database for attached volume " + attachedVolume);
33515619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    } else {
33525619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                        dbhelper.mScanStartTime = SystemClock.currentTimeMicro();
33535619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    }
33545619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                }
33555619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                return attachedVolume;
33565619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            }
3357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3358819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            case MTP_CONNECTED:
335934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                synchronized (mMtpServiceConnection) {
336034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    if (mMtpService == null) {
336134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        Context context = getContext();
336234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        // MTP is connected, so grab a connection to MtpService
336334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        context.bindService(new Intent(context, MtpService.class),
336434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                                mMtpServiceConnection, Context.BIND_AUTO_CREATE);
336534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    }
336634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
3367819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                break;
3368819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
3369afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FILES:
337010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
33712dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_NONE, true, notifyRowIds);
3372fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                if (rowId > 0) {
3373fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                    newUri = Files.getContentUri(uri.getPathSegments().get(0), rowId);
3374fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                }
3375fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                break;
3376fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood
3377e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
33782dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                // We don't send a notification if the insert originated from MTP
337910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
33802dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_NONE, false, notifyRowIds);
3381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (rowId > 0) {
3382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    newUri = Files.getMtpObjectsUri(uri.getPathSegments().get(0), rowId);
33835d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                }
33845d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                break;
33855d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
3386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
3388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
339038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path != null && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
339138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            // need to set the media_type of all the files below this folder to 0
339238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processNewNoMediaPath(helper, db, path);
339338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
3394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
3395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
339738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
339838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Sets the media type of all files below the newly added .nomedia file or
339938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * hidden folder to 0, so the entries no longer appear in e.g. the audio and
340038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * images views.
340138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     *
340238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * @param path The path to the new .nomedia file or hidden directory
340338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
34045461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    private void processNewNoMediaPath(final DatabaseHelper helper, final SQLiteDatabase db,
34055461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            final String path) {
34065461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        final File nomedia = new File(path);
340738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (nomedia.exists()) {
34085461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            hidePath(helper, db, path);
34095461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        } else {
34105461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            // File doesn't exist. Try again in a little while.
34115461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            // XXX there's probably a better way of doing this
34125461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            new Thread(new Runnable() {
34135461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                @Override
34145461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                public void run() {
34155461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    SystemClock.sleep(2000);
34165461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    if (nomedia.exists()) {
34175461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        hidePath(helper, db, path);
34185461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    } else {
34195461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        Log.w(TAG, "does not exist: " + path, new Exception());
34205461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    }
34215461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                }}).start();
342238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
342338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
342438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
34255461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    private void hidePath(DatabaseHelper helper, SQLiteDatabase db, String path) {
34265461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        File nomedia = new File(path);
34275461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        String hiddenroot = nomedia.isDirectory() ? path : nomedia.getParent();
34285461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        ContentValues mediatype = new ContentValues();
34295461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        mediatype.put("media_type", 0);
34305461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        int numrows = db.update("files", mediatype,
34315461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                // the "like" test makes use of the index, while the lower() test ensures it
34325461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                // doesn't match entries it shouldn't when the path contains sqlite wildcards
34335461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                "_data LIKE ? AND lower(substr(_data,1,?))=lower(?)",
34345461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                new String[] { hiddenroot  + "/%",
34355461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    "" + (hiddenroot.length() + 1), hiddenroot + "/"});
34365461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        helper.mNumUpdates += numrows;
34375461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        ContentResolver res = getContext().getContentResolver();
34385461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        res.notifyChange(Uri.parse("content://media/"), null);
34395461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    }
34405461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen
344138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
344238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Rescan files for missing metadata and set their type accordingly.
344338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * There is code for detecting the removal of a nomedia file or renaming of
344438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * a directory from hidden to non-hidden in the MediaScanner and MtpDatabase,
344538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * both of which call here.
344638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
344738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private void processRemovedNoMediaPath(final String path) {
344838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        final DatabaseHelper helper;
344938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path.startsWith(mExternalStoragePaths[0])) {
345038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
345138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        } else {
345238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
345338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
345438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
345538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        new ScannerClient(getContext(), db, path);
345638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
345738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
345838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private static final class ScannerClient implements MediaScannerConnectionClient {
345938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String mPath = null;
346038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        MediaScannerConnection mScannerConnection;
346138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase mDb;
346238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
346338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public ScannerClient(Context context, SQLiteDatabase db, String path) {
346438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mDb = db;
346538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mPath = path;
346638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection = new MediaScannerConnection(context, this);
346738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.connect();
346838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
346938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
347038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
347138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onMediaScannerConnected() {
34725461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            Cursor c = mDb.query("files", openFileColumns,
34735461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    // the "like" test makes use of the index, while the lower() ensures it
34745461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    // doesn't match entries it shouldn't when the path contains sqlite wildcards
34755461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    "_data like ? AND lower(substr(_data,1,?))=lower(?)",
34765461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    new String[] { mPath + "/%", "" + (mPath.length() + 1), mPath + "/"},
34775461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    null, null, null);
347838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            while (c.moveToNext()) {
347938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                String d = c.getString(0);
348038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(d);
348138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (f.isFile()) {
348238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    mScannerConnection.scanFile(d, null);
348338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                }
348438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            }
348538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.disconnect();
348638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            c.close();
348738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
348838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
348938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
349038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onScanCompleted(String path, Uri uri) {
349138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
349238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
349338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3494cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    @Override
3495cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
3496cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                throws OperationApplicationException {
3497cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3498cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // The operations array provides no overall information about the URI(s) being operated
3499cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // on, so begin a transaction for ALL of the databases.
3500cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
3501cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
3502cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase idb = ihelper.getWritableDatabase();
3503cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        idb.beginTransaction();
3504cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase edb = null;
3505cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (ehelper != null) {
3506cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb = ehelper.getWritableDatabase();
3507cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb.beginTransaction();
3508cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3509cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        try {
3510cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentProviderResult[] result = super.applyBatch(operations);
3511cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.setTransactionSuccessful();
3512cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3513cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.setTransactionSuccessful();
3514cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3515cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // Rather than sending targeted change notifications for every Uri
3516cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // affected by the batch operation, just invalidate the entire internal
3517cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // and external name space.
3518cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentResolver res = getContext().getContentResolver();
3519cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
3520cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            return result;
3521cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        } finally {
3522cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.endTransaction();
3523cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3524cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.endTransaction();
3525cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3526cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3527cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    }
3528cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3529cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
35309299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) {
3531b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
3532e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            MediaThumbRequest req = null;
3533e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            try {
3534e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                req = new MediaThumbRequest(
35359299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        getContext().getContentResolver(), path, uri, priority, magic);
3536e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                mMediaThumbQueue.add(req);
3537e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                // Trigger the handler.
3538e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
3539e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                msg.sendToTarget();
3540e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            } catch (Throwable t) {
3541e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Log.w(TAG, t);
3542e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            }
3543b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
3544b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
3545b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
3546b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
3548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
3549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
3550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
3551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
3553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
3554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
3555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
3556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
35579be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            return mExternalStoragePaths[0] + "/" + directoryName + "/" + name + preferredExtension;
3558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private boolean ensureFileExists(String path) {
3562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
3563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
3564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
3565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
3567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
3568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
3569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
3570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
3571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
3572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
3573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
357417ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood            file.getParentFile().mkdirs();
3575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
357617ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood                return file.createNewFile();
3577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
3578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
3579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
3581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
3585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
3586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
3587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
3590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
3591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
3593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
3594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
3595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
35969f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
3597afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3598afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE;
35999f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
36009f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
3601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
3602afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
3604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3606b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
3607b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3608b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
3609b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
3610b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3611b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
3613afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3614afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO;
3615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
3618afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
3623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
3628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
3631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
3632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
3634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
3639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
3642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
3645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
3649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
3654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
3656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
3659afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3660afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST;
3661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3664afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
3669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
3671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
3674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
3676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
3677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
3680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
3681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
3682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
3685afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3686afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO;
3687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
3690afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3694b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
3695b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3696b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
3697b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
3698b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3699b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
370016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
3701e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
37021717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                where = "_id=" + uri.getPathSegments().get(2);
370316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
3704e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
370516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                out.table = "files";
37061717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                break;
37071717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
3708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
3711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
3714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
3715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
3716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
3717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
3718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
3719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
3722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
3726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
3727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
3729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
3733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
3734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
373510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
373610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
373710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
373810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
373910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
374010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStopTime = SystemClock.currentTimeMicro();
3741988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                String msg = dump(database, false);
3742988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                logToDb(database.getWritableDatabase(), msg);
374310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
3745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
3746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3748819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        if (match == VOLUMES_ID) {
3749819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            detachVolume(uri);
3750819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            count = 1;
3751819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else if (match == MTP_CONNECTED) {
375234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (mMtpServiceConnection) {
375334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                if (mMtpService != null) {
375434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // MTP has disconnected, so release our connection to MtpService
375534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    getContext().unbindService(mMtpServiceConnection);
375634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 1;
375734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // mMtpServiceConnection.onServiceDisconnected might not get called,
375834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // so set mMtpService = null here
375934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
376034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } else {
376134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 0;
376234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
376334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
3764819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else {
3765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
3766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
3767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3768819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                        "Unknown URI: " + uri + " match: " + match);
3769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
377010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            database.mNumDeletes++;
3771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
3772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
3774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
3775a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
3776a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                if (sGetTableAndWhereParam.table.equals("files")) {
3777d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
3778d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    if (deleteparam == null || ! deleteparam.equals("false")) {
3779d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        database.mNumQueries++;
3780d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
3781d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sMediaTypeDataId,
3782d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
3783d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        String [] idvalue = new String[] { "" };
37844eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        String [] playlistvalues = new String[] { "", "" };
3785d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        while (c.moveToNext()) {
3786d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            int mediatype = c.getInt(0);
3787d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            if (mediatype == FileColumns.MEDIA_TYPE_IMAGE) {
3788d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                try {
3789d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Libcore.os.remove(c.getString(1));
3790d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    idvalue[0] =  "" + c.getLong(2);
3791d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    database.mNumQueries++;
3792d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Cursor cc = db.query("thumbnails", sDataOnlyColumn,
3793d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                            "image_id=?", idvalue, null, null, null);
3794d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    while (cc.moveToNext()) {
3795d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                        Libcore.os.remove(cc.getString(0));
3796d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    }
3797d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    cc.close();
3798d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    database.mNumDeletes++;
3799d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    db.delete("thumbnails", "image_id=?", idvalue);
3800d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                } catch (ErrnoException e) {
3801a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                                }
3802d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_VIDEO) {
3803d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                try {
3804d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                    Libcore.os.remove(c.getString(1));
3805d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                } catch (ErrnoException e) {
3806d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                }
3807d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_AUDIO) {
3808f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                if (!database.mInternal) {
3809f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                    idvalue[0] =  "" + c.getLong(2);
3810f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                    db.delete("audio_genres_map", "audio_id=?", idvalue);
38114eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    // for each playlist that the item appears in, move
38124eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    // all the items behind it forward by one
38134eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    Cursor cc = db.query("audio_playlists_map",
38144eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                            sPlaylistIdPlayOrder,
38154eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                            "audio_id=?", idvalue, null, null, null);
38164eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    while (cc.moveToNext()) {
38174eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        playlistvalues[0] = "" + cc.getLong(0);
38184eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        playlistvalues[1] = "" + cc.getInt(1);
38194eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        db.execSQL("UPDATE audio_playlists_map" +
38204eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                " SET play_order=play_order-1" +
38214eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                " WHERE playlist_id=? AND play_order>?",
38224eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                playlistvalues);
38234eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    }
38244eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    cc.close();
3825f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                    db.delete("audio_playlists_map", "audio_id=?", idvalue);
3826f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                }
3827d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                            } else if (mediatype == FileColumns.MEDIA_TYPE_PLAYLIST) {
3828d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                // TODO, maybe: remove the audio_playlists_cleanup trigger and implement
3829d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                // it functionality here (clean up the playlist map)
38305afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen                            }
3831a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                        }
3832d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        c.close();
3833a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    }
3834a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                }
3835a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
3836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
383736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    case MTP_OBJECTS:
3838e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood                    case MTP_OBJECTS_ID:
3839d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        try {
3840d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            // don't send objectRemoved event since this originated from MTP
3841d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = true;
384210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            database.mNumDeletes++;
384310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            count = db.delete("files", sGetTableAndWhereParam.where, whereArgs);
3844d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        } finally {
3845d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = false;
3846d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        }
384736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        break;
384878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    case AUDIO_GENRES_ID_MEMBERS:
384910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
385078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        count = db.delete("audio_genres_map",
385178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
385278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        break;
3853166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
3854a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS_ID:
3855a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS:
3856166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS_ID:
3857166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS:
3858166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        // Delete the referenced files first.
3859166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
3860166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sDataOnlyColumn,
3861166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
3862166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        if (c != null) {
3863166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            while (c.moveToNext()) {
3864166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                try {
3865166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                    Libcore.os.remove(c.getString(0));
3866166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                } catch (ErrnoException e) {
3867166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                }
3868166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            }
3869166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            c.close();
3870166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        }
3871166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        database.mNumDeletes++;
3872166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        count = db.delete(sGetTableAndWhereParam.table,
3873166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
3874166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        break;
3875166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
3876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
387710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
3878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
3879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
3880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
3881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
38823631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // Since there are multiple Uris that can refer to the same files
38833631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // and deletes can affect other objects in storage (like subdirectories
38843631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // or playlists) we will notify a change on the entire volume to make
38853631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // sure no listeners miss the notification.
38863631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                String volume = uri.getPathSegments().get(0);
38873631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volume);
38883631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                getContext().getContentResolver().notifyChange(notifyUri, null);
3889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
3893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
389638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    public Bundle call(String method, String arg, Bundle extras) {
389738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (MediaStore.UNHIDE_CALL.equals(method)) {
389838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processRemovedNoMediaPath(arg);
389938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            return null;
390038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
390138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        throw new UnsupportedOperationException("Unsupported call: " + method);
390238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
390338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
390438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    @Override
3905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
3906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
3907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3908b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
3909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
391010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
391110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
3912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
391510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumUpdates++;
391610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
391710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
3918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3919b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
3920b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3921b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3922b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
3923b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3924b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
3926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
3927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
39281d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // special case renaming directories via MTP.
39291d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // in this case we must update all paths in the database with
39301d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // the directory name as a prefix
39311d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID)
39321d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    && initialValues != null && initialValues.size() == 1) {
39331d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                String oldPath = null;
3934801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA);
39357f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.remove(newPath);
39361d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                // MtpDatabase will rename the directory first, so we test the new file name
393738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(newPath);
393838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (newPath != null && f.isDirectory()) {
393910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumQueries++;
39401d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION,
39411d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        userWhere, whereArgs, null, null, null);
39421d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    try {
39431d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null && cursor.moveToNext()) {
39441d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            oldPath = cursor.getString(1);
39451d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
39461d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    } finally {
39471d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null) cursor.close();
39481d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
39491d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    if (oldPath != null) {
39507f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                        mDirectoryCache.remove(oldPath);
39511d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        // first rename the row for the directory
395210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
39531d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        count = db.update(sGetTableAndWhereParam.table, initialValues,
39541d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
39551d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0) {
39561d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            // then update the paths of any files and folders contained in the directory.
39575461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                            Object[] bindArgs = new Object[] {newPath, oldPath.length() + 1,
39585461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    oldPath + "/%", (oldPath.length() + 1), oldPath + "/"};
395910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
39605461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                            db.execSQL("UPDATE files SET _data=?1||SUBSTR(_data, ?2)" +
39615461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    // the "like" test makes use of the index, while the lower()
39625461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    // test ensures it doesn't match entries it shouldn't when the
39635461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    // path contains sqlite wildcards
39645461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    " WHERE _data LIKE ?3 AND lower(substr(_data,1,?4))=lower(?5);",
39655461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    bindArgs);
39661d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
39671d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
39681d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0 && !db.inTransaction()) {
39691d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            getContext().getContentResolver().notifyChange(uri, null);
39701d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
397138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        if (f.getName().startsWith(".")) {
397238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            // the new directory name is hidden
397338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            processNewNoMediaPath(helper, db, newPath);
397438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        }
39751d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        return count;
39761d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
397738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                } else if (newPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
397838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    processNewNoMediaPath(helper, db, newPath);
39791d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                }
39801d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            }
39811d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
3982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
3983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
3984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
3985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
3986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
39872658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
39882658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
39892658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        values.remove(MediaStore.Audio.Media.COMPILATION);
399007656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen
3991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
3992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
3993a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
39946006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("artist");
3995a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
3996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
399710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> artistCache = helper.mArtistCache;
3998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
3999a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
4000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
400110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    artistRowId = getKeyIdForName(helper, db,
400210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "artists", "artist_key", "artist",
4003acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
4004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
4005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
4006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
4007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
4009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
401159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
4012a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
40136006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("album");
4014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
4015801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                            String path = values.getAsString(MediaStore.MediaColumns.DATA);
401659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
40172658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            if (albumartist != null) {
40182658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                albumHash = albumartist.hashCode();
40192658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            } else if (compilation != null && compilation.equals("1")) {
40202658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                // nothing to do, hash already set
402159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
40229289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path == null) {
40239289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    if (match == AUDIO_MEDIA) {
40249289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Log.w(TAG, "Possible multi row album name update without"
40259289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                + " path could give wrong album key");
40269289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    } else {
40279289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        //Log.w(TAG, "Specify path to avoid extra query");
40289289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Cursor c = query(uri,
40299289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                new String[] { MediaStore.Audio.Media.DATA},
40309289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                null, null, null);
40319289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        if (c != null) {
40329289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            try {
40339289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                int numrows = c.getCount();
40349289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                if (numrows == 1) {
40359289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    c.moveToFirst();
40369289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    path = c.getString(0);
40379289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                } else {
40389289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    Log.e(TAG, "" + numrows + " rows for " + uri);
40399289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                }
40409289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            } finally {
40419289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                c.close();
40429289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            }
40439289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        }
40449289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    }
40459289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
40469289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path != null) {
40479289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
40489289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
404959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
40502658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
4051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
4052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
405310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> albumCache = helper.mAlbumCache;
4054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
405559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
405659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
4057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
405810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    albumRowId = getKeyIdForName(helper, db,
405910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "albums", "album_key", "album",
4060a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
4061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
4062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
4063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
4064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
4066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
4069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
4070702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
4071702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
4072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
4073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
4074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
4075e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
4076e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
4077e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
4078e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
4079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
408110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
4082afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        count = db.update(sGetTableAndWhereParam.table, values,
4083afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
4084b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        if (genre != null) {
4085b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            if (count == 1 && match == AUDIO_MEDIA_ID) {
4086b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                long rowId = Long.parseLong(uri.getPathSegments().get(3));
4087b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                updateGenre(rowId, genre);
4088b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            } else {
4089b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                // can't handle genres for bulk update or for non-audio files
4090b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                Log.w(TAG, "ignoring genre in update: count = "
4091b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                        + count + " match = " + match);
4092b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            }
4093b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        }
4094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
4097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
4098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
4099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
4100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
4101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
4102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
4103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
4104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
4105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
4106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
4107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
4108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
4109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
4110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
4111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4112b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                        computeTakenTime(values);
411310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
4114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
4115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
411601a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
411701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
411801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
411901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
412010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumQueries++;
412101a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
412201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
412301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
4124b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
4125216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                try {
4126216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    while (c.moveToNext()) {
4127216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        long magic = c.getLong(2);
4128216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        if (magic == 0) {
4129216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                            requestMediaThumbnail(c.getString(1), uri,
4130216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                                    MediaThumbRequest.PRIORITY_NORMAL, 0);
4131216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        }
4132b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
4133216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                } finally {
4134216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    c.close();
4135b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
4136b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
4137b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
4138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4140f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4141f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
4142f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    String moveit = uri.getQueryParameter("move");
4143f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    if (moveit != null) {
4144f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
4145f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        if (initialValues.containsKey(key)) {
4146f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int newpos = initialValues.getAsInteger(key);
4147f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            List <String> segments = uri.getPathSegments();
4148f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            long playlist = Long.valueOf(segments.get(3));
4149f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int oldpos = Integer.valueOf(segments.get(5));
415010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            return movePlaylistEntry(helper, db, playlist, oldpos, newpos);
4151f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        }
4152f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        throw new IllegalArgumentException("Need to specify " + key +
4153f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                                " when using 'move' parameter");
4154f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    }
4155f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    // fall through
4156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
415710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumUpdates++;
4158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
4159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
4160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4163cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // in a transaction, the code that began the transaction should be taking
4164cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // care of notifications once it ends the transaction successfully
4165cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (count > 0 && !db.inTransaction()) {
4166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
4167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
4169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
417110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int movePlaylistEntry(DatabaseHelper helper, SQLiteDatabase db,
417210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            long playlist, int from, int to) {
4173f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        if (from == to) {
4174f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return 0;
4175f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
4176f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        db.beginTransaction();
4177f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        try {
4178f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            int numlines = 0;
417910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates += 3;
41804eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            Cursor c = db.query("audio_playlists_map",
41814eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    new String [] {"play_order" },
41824eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order",
41834eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    from + ",1");
41844eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.moveToFirst();
41854eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            int from_play_order = c.getInt(0);
41864eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.close();
41874eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c = db.query("audio_playlists_map",
41884eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    new String [] {"play_order" },
41894eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order",
41904eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    to + ",1");
41914eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.moveToFirst();
41924eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            int to_play_order = c.getInt(0);
41934eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.close();
4194f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=-1" +
41954eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    " WHERE play_order=" + from_play_order +
4196f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " AND playlist_id=" + playlist);
4197f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // We could just run both of the next two statements, but only one of
4198f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // of them will actually do anything, so might as well skip the compile
4199f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // and execute steps.
4200f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            if (from  < to) {
4201f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" +
42024eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " WHERE play_order<=" + to_play_order +
42034eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " AND play_order>" + from_play_order +
4204f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4205f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = to - from + 1;
4206f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            } else {
4207f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" +
42084eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " WHERE play_order>=" + to_play_order +
42094eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " AND play_order<" + from_play_order +
4210f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4211f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = from - to + 1;
4212f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            }
42134eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=" + to_play_order +
4214f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=-1 AND playlist_id=" + playlist);
4215f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.setTransactionSuccessful();
4216f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
4217f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    .buildUpon().appendEncodedPath(String.valueOf(playlist)).build();
4218f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            getContext().getContentResolver().notifyChange(uri, null);
4219f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return numlines;
4220f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        } finally {
4221f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.endTransaction();
4222f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
4223f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    }
4224f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
4226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
4227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
4228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
4230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
4231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
423271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
423471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
423571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
423671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
423771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
423871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
423971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
424071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
424171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
42425fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            if (db == null) {
42435fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                throw new IllegalStateException("Couldn't open database for " + uri);
42445fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            }
424571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
424671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
424771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
424871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
424971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
425071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
425171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
425271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
425371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
425471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
425571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                String audiopath = c.getString(0);
425671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                int albumid = c.getInt(1);
425771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // Try to get existing album art for this album first, which
425871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // could possibly have been obtained from a different file.
425971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // If that fails, try to get it from this specific file.
426071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
426171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
4262007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    pfd = openFileAndEnforcePathPermissionsHelper(newUri, mode);
426371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
426471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    // That didn't work, now try to get it from the specific file
426534fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen                    pfd = getThumb(database, db, audiopath, albumid, null);
426671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
426771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
426871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            c.close();
426971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
427071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
427171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4273007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            pfd = openFileAndEnforcePathPermissionsHelper(uri, mode);
4274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
427571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
427671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
427771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
427871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
427971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
4281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
4282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
4283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
4284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
4285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
42875fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                if (db == null) {
42885fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                    throw new IllegalStateException("Couldn't open database for " + uri);
42895fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                }
4290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
429271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
4293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
4294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
4295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
4296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
4297a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen                        null, null, null, null, MediaStore.Audio.Media.TRACK);
429871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                if (c.moveToFirst()) {
4299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String audiopath = c.getString(0);
430034fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen                    pfd = getThumb(database, db, audiopath, albumid, uri);
4301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                c.close();
4303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
430471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
430571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
430671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
4307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
4309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4311007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /**
4312007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * Return the {@link MediaColumns#DATA} field for the given {@code Uri}.
4313007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     */
4314007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private File queryForDataFile(Uri uri) throws FileNotFoundException {
4315007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final Cursor cursor = query(
4316007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                uri, new String[] { MediaColumns.DATA }, null, null, null);
43178ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey        if (cursor == null) {
43188ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey            throw new FileNotFoundException("Missing cursor for " + uri);
43198ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey        }
43208ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey
4321007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
4322007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            switch (cursor.getCount()) {
4323007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                case 0:
4324007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    throw new FileNotFoundException("No entry for " + uri);
4325007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                case 1:
4326007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    if (cursor.moveToFirst()) {
4327007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        return new File(cursor.getString(0));
4328007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    } else {
4329007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        throw new FileNotFoundException("Unable to read entry for " + uri);
4330007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    }
4331007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                default:
4332007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    throw new FileNotFoundException("Multiple items at " + uri);
4333007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            }
4334007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } finally {
4335007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            cursor.close();
4336007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4337007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
4338007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4339007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /**
4340007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * Replacement for {@link #openFileHelper(Uri, String)} which enforces any
4341007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * permissions applicable to the path before returning.
4342007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     */
4343007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, String mode)
4344007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throws FileNotFoundException {
4345007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final int modeBits = ContentResolver.modeToMode(uri, mode);
4346007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0;
4347007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4348f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey        File file = queryForDataFile(uri);
4349007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final String path;
4350007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
4351007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            path = file.getCanonicalPath();
4352007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } catch (IOException e) {
4353007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throw new IllegalArgumentException("Unable to resolve canonical path for " + file, e);
4354007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4355007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4356007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        if (path.startsWith(sExternalPath)) {
4357007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            getContext().enforceCallingOrSelfPermission(
4358007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    READ_EXTERNAL_STORAGE, "External path: " + path);
4359007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4360007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            if (isWrite) {
4361007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                getContext().enforceCallingOrSelfPermission(
4362007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        WRITE_EXTERNAL_STORAGE, "External path: " + path);
4363007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            }
4364f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey
4365dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey            // bypass emulation layer when file is opened for reading, but only
4366dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey            // when opening read-only and we have an exact match.
4367f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey            if (modeBits == MODE_READ_ONLY && Environment.isExternalStorageEmulated()) {
4368dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey                final File directFile = new File(Environment.getMediaStorageDirectory(), path
4369dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey                        .substring(sExternalPath.length()));
4370dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey                if (directFile.exists()) {
4371dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey                    file = directFile;
4372dfd97076b0542e8f27a62e4ba64e1121c8b127f5Jeff Sharkey                }
4373f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey            }
4374f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey
4375007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } else if (path.startsWith(sCachePath)) {
4376007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            getContext().enforceCallingOrSelfPermission(
4377007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    ACCESS_CACHE_FILESYSTEM, "Cache path: " + path);
4378007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4379007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4380007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        return ParcelFileDescriptor.open(file, modeBits);
4381007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
4382007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
438410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper;
4385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
4386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
4387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
4388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
4389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
439110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void makeThumbAsync(DatabaseHelper helper, SQLiteDatabase db,
439210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String path, long album_id) {
43938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
43948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
43958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
43968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
43978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
43988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
43998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
44008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
44018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
44028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
4403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
440410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        d.helper = helper;
4405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
4406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
4407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
4408a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
44098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
44108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
44118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
44128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
44138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
44148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
44158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
44168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
44178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
44188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
44198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
4420b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
4421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
4422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
44248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
44258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
44268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
44278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
4428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
4431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
4432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
4433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
44348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
44358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
4436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
4437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4438d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
44393f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // same directory as the media file, except if that directory is
44403f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // is the root directory of the sd card or the download directory.
4441d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
4442d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
4443d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
4444d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
4445d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
4446d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
44478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
4448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
4449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
4450d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
44513f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String artPath = path.substring(0, lastSlash);
44529be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    String sdroot = mExternalStoragePaths[0];
44530fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen                    String dwndir = Environment.getExternalStoragePublicDirectory(
44542f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
4455d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4456d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
4457d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
4458d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
4459d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
4460ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                        } else if (!artPath.equalsIgnoreCase(sdroot) &&
4461ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                                !artPath.equalsIgnoreCase(dwndir)) {
4462d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
4463d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
4464d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
4465d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
4466d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4467d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
4468d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
4469d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
4470d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
4471d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
4472d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4473d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
4474d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
4475d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
4476d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
4477d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4478d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
4479d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
4480d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
4481d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
4482d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4483d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
4484d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
4485d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4486d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
4487d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
4488d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4489d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
4490d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4491d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4492d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
4493d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
4494d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
4495d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
4496d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4497d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
44983f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        File file = new File(artPath, bestmatch);
4499d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
4500d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            compressed = new byte[(int)file.length()];
4501d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
4502d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
4503d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
4504d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
4505d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
4506d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
4507d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
4508d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
4509d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
4510d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
45168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
45178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
4518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
45198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
45208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
4521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
45228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
452310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    Uri getAlbumArtOutputUri(DatabaseHelper helper, SQLiteDatabase db, long album_id, Uri albumart_uri) {
45248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
45258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
45268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
45278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
4528801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            Cursor c = query(albumart_uri, new String [] { MediaStore.MediaColumns.DATA },
45298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
4530d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            try {
4531d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null && c.moveToFirst()) {
4532d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    String albumart_path = c.getString(0);
4533d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    if (ensureFileExists(albumart_path)) {
4534d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                        out = albumart_uri;
4535d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    }
4536d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                } else {
4537d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    albumart_uri = null;
4538d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                }
4539d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            } finally {
4540d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null) {
4541d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    c.close();
4542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
454471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
454571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
45468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
45478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
45488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
45498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
455010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
4551801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
45528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
45538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
4554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
45558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
45568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
45578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
45588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
45598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
45608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
45618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
45628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
45638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
45648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
45658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) {
45668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean success = false;
45678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
45688a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            OutputStream outstream = getContext().getContentResolver().openOutputStream(out);
45698a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
45708a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
45718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
45728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
45738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
45748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = true;
45758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
457670676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann                success = bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream);
4577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
45788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
45798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            outstream.close();
45808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (FileNotFoundException ex) {
45818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
4582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (IOException ex) {
45838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
45848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
45858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (!success) {
45868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // the thumbnail was not written successfully, delete the entry that refers to it
45878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            getContext().getContentResolver().delete(out, null, null);
4588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
45898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
45908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
459134fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen    private ParcelFileDescriptor getThumb(DatabaseHelper helper, SQLiteDatabase db, String path,
459234fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen            long album_id, Uri albumart_uri) {
459371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
459434fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen        d.helper = helper;
459571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
459671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
459771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
459871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
459971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
460071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
460171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
460271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
46038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
4604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
46058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
460671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
46078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
46088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
46098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
46108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
46118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
46128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
46138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
46148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
46158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
46168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
46178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
46188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
46198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
462070676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final Resources r = getContext().getResources();
462170676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size);
462270676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) {
46238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
46248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
46258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
46268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
46278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
46288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
46298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
46308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
46318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
46328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
46338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
46348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
46358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
46368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
46378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
46388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
4639a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false);
4640a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    if (nbm != null && nbm != bm) {
4641a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm.recycle();
4642a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm = nbm;
4643a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    }
46448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
46458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
46468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
46478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
46488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
46498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
465071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
46518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
46528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
465371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
465471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
465571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
465671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
465724001394f571b1f0378840cbf299288e4df10508Bjorn Bringert                return ParcelFileDescriptor.fromData(compressed, "albumthumb");
465871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
465971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
466071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
4661a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This one needs to actually be saved on the sd card.
4662a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This is wrapped in a transaction because there are various things
4663a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // that could go wrong while generating the thumbnail, and we only want
4664a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // to update the database when all steps succeeded.
4665a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            d.db.beginTransaction();
4666a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            try {
466710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Uri out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri);
4668a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen
4669a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (out != null) {
4670a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    writeAlbumArt(need_to_recompress, out, compressed, bm);
4671a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    getContext().getContentResolver().notifyChange(MEDIA_URI, null);
4672a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    ParcelFileDescriptor pfd = openFileHelper(out, "r");
4673a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    d.db.setTransactionSuccessful();
4674a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    return pfd;
4675a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                }
4676a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (FileNotFoundException ex) {
4677a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
4678a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (UnsupportedOperationException ex) {
4679a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
4680a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } finally {
4681a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                d.db.endTransaction();
4682a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (bm != null) {
4683a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    bm.recycle();
468471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
468571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
46868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
468771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
4688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
4692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
4693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
4694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
4695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
4696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
4697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
469859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
469959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
470059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
4701a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
4702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
4703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
4704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
4705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
4706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
470710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getKeyIdForName(DatabaseHelper helper, SQLiteDatabase db,
470810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String table, String keyField, String nameField,
470959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
4710a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
4711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
4712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
471451cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            rawName = MediaStore.UNKNOWN_STRING;
4715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
4717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
471951cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            // shouldn't happen, since we only get null keys for null inputs
472051cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            Log.e(TAG, "null key", new Exception());
4721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
4722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
472459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
4725e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen        boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName);
472659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
47272658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // To distinguish same-named albums, we append a hash. The hash is based
47282658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // on the "album artist" tag if present, otherwise on the "compilation" tag
47292658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // if present, otherwise on the path.
473059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
473159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
473259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
473359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
473459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
4735a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
473659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
4737a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
4738a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
4739a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
474059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
474159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
4742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
474310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
4744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
4745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
4748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
4749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
4750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
4751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
4752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
475310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumInserts++;
4754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
475559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
4756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
475710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            makeThumbAsync(helper, db, path, rowId);
4758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
4760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
4761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
4762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
4763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
4767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
4768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
4769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
4770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
4772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
4773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
4774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
4775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
4776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
4777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
4778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
477910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
4780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
4781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
4782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
4783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
4784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
4788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
4789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
4790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
4791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
4794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (c != null) c.close();
4795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
479759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
479859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
4799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
4801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
4805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
4806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
4807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
4808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
4809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
4811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
4812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
4813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
4814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
4816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
4817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
4818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
4820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
4821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
4823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
4824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
4825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
4826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
4827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
4828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
4829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
4830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
4831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
4835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
4836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
4837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
4838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
4839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
4840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
4843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
4844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
4849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
4851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
4852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
4854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
48555619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            if (uri.getPathSegments().size() >= 1) {
4856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
4857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
4860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4862fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isMediaDatabaseName(String name) {
4863fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
4864fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4865fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4866fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (EXTERNAL_DATABASE_NAME.equals(name)) {
4867fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4868fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4869fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (name.startsWith("external-")) {
4870fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4871fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4872fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
4873fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
4874fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
4875fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isInternalMediaDatabaseName(String name) {
4876fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
4877fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4878fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4879fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
4880fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
4881fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
4882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
4884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
4885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
4886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
4888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
4889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
489175392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
4892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
4893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
4894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
4897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
4898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
4899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4901993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            Context context = getContext();
490210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper helper;
4903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
490410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true,
4905fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                        false, mObjectRemovedCallback);
4906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
4907993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                if (Environment.isExternalStorageRemovable()) {
49089be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    String path = mExternalStoragePaths[0];
4909993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    int volumeID = FileUtils.getFatVolumeId(path);
4910993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
4911993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
4912993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // generate database name based on volume ID
4913993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
491410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbName, false,
4915fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
4916993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    mVolumeId = volumeID;
4917993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                } else {
4918993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // external database name should be EXTERNAL_DATABASE_NAME
4919993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // however earlier releases used the external-XXXXXXXX.db naming
4920993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // for devices without removable storage, and in that case we need to convert
4921993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // to this new convention
4922993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME);
4923993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (!dbFile.exists()) {
4924993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // find the most recent external database and rename it to
4925993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // EXTERNAL_DATABASE_NAME, and delete any other older
4926993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // external database files
4927993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        File recentDbFile = null;
4928993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        for (String database : context.databaseList()) {
4929993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (database.startsWith("external-")) {
4930993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                File file = context.getDatabasePath(database);
4931993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                if (recentDbFile == null) {
4932993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
4933993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else if (file.lastModified() > recentDbFile.lastModified()) {
4934993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile.delete();
4935993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
4936993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else {
4937993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    file.delete();
4938993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                }
4939993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
4940993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
4941993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        if (recentDbFile != null) {
4942993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (recentDbFile.renameTo(dbFile)) {
4943993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.d(TAG, "renamed database " + recentDbFile.getName() +
4944993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
4945993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            } else {
4946993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.e(TAG, "Failed to rename database " + recentDbFile.getName() +
4947993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
4948993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // This shouldn't happen, but if it does, continue using
4949993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // the file under its old name
4950993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                dbFile = recentDbFile;
4951993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
4952993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
4953993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME
4954993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    }
495510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbFile.getName(), false,
4956fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
4957993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                }
4958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
4959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
4960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
496210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            mDatabases.put(volume, helper);
4963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
496410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (!helper.mInternal) {
4965ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                // create default directories (only happens on first boot)
496610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                createDefaultFolders(helper, helper.getWritableDatabase());
4967ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
4968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
49699be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                File[] files = new File(mExternalStoragePaths[0], ALBUM_THUMB_FOLDER).listFiles();
4970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
4971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
4972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
4973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
4976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
4977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
4978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
4979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
4980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
4982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (cursor != null) cursor.close();
4983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
4986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
4987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
4988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
4989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
4990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
4995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
4996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
5000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
5001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
5002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
5004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
500675392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
5007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
5008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
5009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
5012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
5013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
5014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
5015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
5016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
5017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
5018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
5021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
5022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
5023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
5025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
5026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
5027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
5028e9ee0248d62f3badef8a554f35f78e9116ef8a5cMike Lockwood            } catch (Exception e) {
5029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
5030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
5033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
5034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
5037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
5038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
5041ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer    private static final boolean LOCAL_LOGV = false;
5042971a2ef5165e2072c76bf25049fdda94187019c2Dianne Hackborn
5043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
5044993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private static final String EXTERNAL_DATABASE_NAME = "external.db";
5045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
5047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
5048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
5050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
5051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
5052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
5054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
5056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
5058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
5059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
50600027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    // current FAT volume ID
5061993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private int mVolumeId = -1;
50620027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
5063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
5064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
5065268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen    static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs";
5066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
5068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
5069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
50701717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS
507116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    // are stored in the "files" table, so do not renumber them unless you also add
50721717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // a corresponding database upgrade step for it.
5073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
5074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
5075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
5076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
5077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
5079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
5080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
5081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
5082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
5083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
5084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
5085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
5086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
5087bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen    private static final int AUDIO_GENRES_ALL_MEMBERS = 109;
5088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
5089702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
5090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
5091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
5092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
5093702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
5094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
5095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
5096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
5097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
5098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
509971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
5100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
5102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
5103b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
5104b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
5105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
5107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
5108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
5110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
5111a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
5112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
5114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
51150027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private static final int FS_ID = 600;
5116704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen    private static final int VERSION = 601;
51170027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
511816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES = 700;
511916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES_ID = 701;
5120a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
5121e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    // Used only by the MTP implementation
5122e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS = 702;
5123e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS_ID = 703;
5124e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECT_REFERENCES = 704;
5125819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // UsbReceiver calls insert() and delete() with this URI to tell us
5126819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // when MTP is connected and disconnected
5127819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    private static final int MTP_CONNECTED = 705;
5128b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
5129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
5130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
5131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5132b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
5133b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
5134b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
5135b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
51361d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    private static final String[] PATH_PROJECTION = new String[] {
51371d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood        MediaStore.MediaColumns._ID,
51381d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            MediaStore.MediaColumns.DATA,
51391d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    };
51401d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
5141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
5142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
5143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
5144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
5145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5146b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
5147b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
5148b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
5149b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
5150b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
5151b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
5152e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private static final String OBJECT_REFERENCES_QUERY =
5153afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map"
5154afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?"
5155afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER;
5156e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
5157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
5158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
5159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
5160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
5161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
5162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
5163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
5165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
5166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
5167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
5168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
5169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
5170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
5171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
5172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
5173bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen        URI_MATCHER.addURI("media", "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS);
5174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
5175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
5176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
5177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
5178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
5179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
5180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
5181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
5182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
5183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
5184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
518571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
5186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
5188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
5189b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
5190b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
5191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
5193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
51940027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        URI_MATCHER.addURI("media", "*/fs_id", FS_ID);
5195704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        URI_MATCHER.addURI("media", "*/version", VERSION);
51960027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
5197819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED);
5198819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
5199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
5200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
5201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5202b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        // Used by MTP implementation
520316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file", FILES);
520416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file/#", FILES_ID);
5205e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS);
5206e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID);
5207e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES);
5208b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
5209a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
5210a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
5211a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
5212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
5213a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
5214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
5215a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
5216a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
5217a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
5218a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
5219a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
5220a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
5221a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
5222a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
5223a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
5224a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
5225a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
5226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
522710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
522810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    @Override
522910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
523010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        Collection<DatabaseHelper> foo = mDatabases.values();
523110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        for (DatabaseHelper dbh: foo) {
5232988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            writer.println(dump(dbh, true));
5233988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        }
5234988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        writer.flush();
5235988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    }
5236988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
5237988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    private String dump(DatabaseHelper dbh, boolean dumpDbLog) {
5238988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        StringBuilder s = new StringBuilder();
5239988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        s.append(dbh.mName);
5240988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        s.append(": ");
5241988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        SQLiteDatabase db = dbh.getReadableDatabase();
5242988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        if (db == null) {
5243988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append("null");
5244988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        } else {
5245988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append("version " + db.getVersion() + ", ");
5246988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            Cursor c = db.query("files", new String[] {"count(*)"}, null, null, null, null, null);
5247988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            try {
5248988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (c != null && c.moveToFirst()) {
5249988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    int num = c.getInt(0);
5250988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    s.append(num + " rows, ");
5251988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                } else {
5252988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    s.append("couldn't get row count, ");
5253988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5254988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            } finally {
5255988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (c != null) {
5256988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    c.close();
5257988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5258988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            }
5259988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumInserts + " inserts, ");
5260988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumUpdates + " updates, ");
5261988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumDeletes + " deletes, ");
5262988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumQueries + " queries, ");
5263988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            if (dbh.mScanStartTime != 0) {
5264988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                s.append("scan started " + DateUtils.formatDateTime(getContext(),
5265988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        dbh.mScanStartTime / 1000,
5266988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        DateUtils.FORMAT_SHOW_DATE
5267988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        | DateUtils.FORMAT_SHOW_TIME
5268988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        | DateUtils.FORMAT_ABBREV_ALL));
5269988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                long now = dbh.mScanStopTime;
5270988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (now < dbh.mScanStartTime) {
5271988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    now = SystemClock.currentTimeMicro();
5272988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5273988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                s.append(" (" + DateUtils.formatElapsedTime(
5274988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        (now - dbh.mScanStartTime) / 1000000) + ")");
5275988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (dbh.mScanStopTime < dbh.mScanStartTime) {
5276988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    if (mMediaScannerVolume != null &&
5277988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            dbh.mName.startsWith(mMediaScannerVolume)) {
5278988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        s.append(" (ongoing)");
527910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    } else {
5280988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        s.append(" (scanning " + mMediaScannerVolume + ")");
5281988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    }
5282988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5283988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            }
5284988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            if (dumpDbLog) {
5285988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                c = db.query("log", new String[] {"time", "message"},
5286988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        null, null, null, null, "time");
5287988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                try {
5288988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    if (c != null) {
5289988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        while (c.moveToNext()) {
5290988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            String when = c.getString(0);
5291988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            String msg = c.getString(1);
5292988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            s.append("\n" + when + " : " + msg);
5293988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        }
529410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
529510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                } finally {
529610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (c != null) {
529710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        c.close();
529810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
529910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                }
530010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
530110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        }
5302988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        return s.toString();
530310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    }
5304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
5305