MediaProvider.java revision 10af34f31704509a71d02b0b4a15cfa07bbfede3
1702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/*
2702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
3702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
4702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * you may not use this file except in compliance with the License.
6702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * You may obtain a copy of the License at
7702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
8702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
10702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * See the License for the specific language governing permissions and
14702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * limitations under the License.
15702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
16702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
17702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpackage com.android.providers.media;
18702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
19702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.app.SearchManager;
20bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.BroadcastReceiver;
21bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ComponentName;
22bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProvider;
23bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderOperation;
24bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderResult;
25bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentResolver;
26bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentUris;
27bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentValues;
28bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Context;
29bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Intent;
30bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.IntentFilter;
31bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.OperationApplicationException;
32bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ServiceConnection;
33ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.content.SharedPreferences;
34bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.UriMatcher;
3570676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.content.res.Resources;
36702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
37ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils;
380027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor;
39702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
40702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
41702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
42702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap;
43702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory;
44b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.media.MediaFile;
45702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner;
46b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
4790345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpConstants;
489be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwoodimport android.mtp.MtpStorage;
49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
51702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
52702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils;
53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler;
54ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread;
55702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message;
56702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor;
57702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process;
58d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException;
5910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.os.SystemClock;
604f2186758ee1c6eaa702bf1511b233b26143b631Mike Lockwoodimport android.os.SystemProperties;
61c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwoodimport android.os.storage.StorageManager;
621f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwoodimport android.os.storage.StorageVolume;
63ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.preference.PreferenceManager;
64702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns;
65702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore;
66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio;
675bf5c26a3499d584332074baab97392696ed9614Ray Chenimport android.provider.MediaStore.Files;
6870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Files.FileColumns;
69702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images;
7070676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Images.ImageColumns;
71702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns;
72702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video;
73702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
7410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.text.format.DateUtils;
75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
76702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
77702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
7810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.FileDescriptor;
79702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
80702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
81702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
8310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.PrintWriter;
84702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.text.Collator;
85cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList;
8610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.util.Collection;
87702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
88702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
89702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
90f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List;
91b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
93702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
94702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/**
95702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details.
96702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the
97702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index).  The content visible at content://media/external/...
98702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card.
99702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider {
101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri MEDIA_URI = Uri.parse("content://media");
102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");
103b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int ALBUM_THUMB = 1;
104b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int IMAGE_THUMB = 2;
105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>();
107d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen    private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>();
108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1097f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    // In memory cache of path<->id mappings, to speed up inserts during media scan
1107f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>();
1117f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
1128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
1138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
1148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
1158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
1168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
1178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
11820434e032e498b716f87cce2f23dd646819218bfRay Chen    // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest.
11920434e032e498b716f87cce2f23dd646819218bfRay Chen    private MediaThumbRequest mCurrentThumbRequest = null;
120b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
121b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
122b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
123b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
124f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood    private boolean mCaseInsensitivePaths;
1259be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private static String[] mExternalStoragePaths;
12617ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood
127a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
128a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
129a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
130a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
131a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
132a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
133a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
134a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
135a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
136a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
137a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
138a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
139a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
140a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
141a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
142a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
143a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
144a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
145ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
146ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
147a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
148a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
149a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
150a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
151a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
152a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
153a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
154a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
155a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
156a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
15763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
158a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
159a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
160a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
161a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
164a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
166a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
16763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
16863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
169e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen            " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" +
17063f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
171a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
172a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
17363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
17463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
175a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1761717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    private static final String[] mMediaTableColumns = new String[] {
17716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            FileColumns._ID,
178afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            FileColumns.MEDIA_TYPE,
1791717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    };
1801717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
1817f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    private static final String[] sIdOnlyColumn = new String[] {
1827f36494e085c26c69cd5925e54028822025eff29Marco Nelissen        FileColumns._ID
1837f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    };
1847f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
185a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
186a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
1911f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                StorageVolume storage = (StorageVolume)intent.getParcelableExtra(
1921f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        StorageVolume.EXTRA_STORAGE_VOLUME);
1931f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // If primary external storage is ejected, then remove the external volume
1941f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // notify all cursors backed by data on that volume.
1951f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                if (storage.getPath().equals(mExternalStoragePaths[0])) {
1961f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    detachVolume(Uri.parse("content://media/external"));
1971f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    sFolderArtMap.clear();
1981f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    MiniThumbFile.reset();
1991f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                } else {
2001f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // If secondary external storage is ejected, then we delete all database
2011f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // entries for that storage from the files table.
2021f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    synchronized (mDatabases) {
2031f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        DatabaseHelper database = mDatabases.get(EXTERNAL_VOLUME);
2041f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        Uri uri = Uri.parse("file://" + storage.getPath());
2051f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        if (database != null) {
2061f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            try {
2071f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // Send media scanner started and stopped broadcasts for apps that rely
2081f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // on these Intents for coarse grained media database notifications.
2091f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2101f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
2111f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood
2121f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // don't send objectRemoved events - MTP be sending StorageRemoved anyway
2131f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = true;
2141f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.d(TAG, "deleting all entries for storage " + storage);
2151f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                SQLiteDatabase db = database.getWritableDatabase();
2164b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // First clear the file path to disable the _DELETE_FILE database hook.
2174b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // We do this to avoid deleting files if the volume is remounted while
2184b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // we are still processing the unmount event.
2194b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                ContentValues values = new ContentValues();
2204b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                values.put(Files.FileColumns.DATA, "");
2214b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String where = FileColumns.STORAGE_ID + "=?";
2224b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) };
2234b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                db.update("files", values, where, whereArgs);
2244b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // now delete the records
2254b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                db.delete("files", where, whereArgs);
2264b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // notify on media Uris as well as the files Uri
2274b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2284b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Audio.Media.getContentUri(EXTERNAL_VOLUME), null);
2294b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2304b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Images.Media.getContentUri(EXTERNAL_VOLUME), null);
2314b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2324b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Video.Media.getContentUri(EXTERNAL_VOLUME), null);
2331f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.getContentResolver().notifyChange(
2341f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        Files.getContentUri(EXTERNAL_VOLUME), null);
2351f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } catch (Exception e) {
2361f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.e(TAG, "exception deleting storage entries", e);
2371f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } finally {
2381f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2391f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
2401f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = false;
2411f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            }
2421f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        }
2431f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    }
2441f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                }
245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
249d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    // set to disable sending events when the operation originates from MTP
250d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private boolean mDisableMtpObjectCallbacks;
251d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
252d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final SQLiteDatabase.CustomFunction mObjectRemovedCallback =
253d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                new SQLiteDatabase.CustomFunction() {
254d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void callback(String[] args) {
2557f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // We could remove only the deleted entry from the cache, but that
2567f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // requires the path, which we don't have here, so instead we just
2577f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // clear the entire cache.
2587f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: include the path in the callback and only remove the affected
2597f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // entry from the cache
2607f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            mDirectoryCache.clear();
261d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // do nothing if the operation originated from MTP
262d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mDisableMtpObjectCallbacks) return;
263d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
264d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "object removed " + args[0]);
265d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            IMtpService mtpService = mMtpService;
266d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mtpService != null) {
267d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                try {
268d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    sendObjectRemoved(Integer.parseInt(args[0]));
269d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                } catch (NumberFormatException e) {
270d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e);
271d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
272d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
273d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
274d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
275d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
281fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static final class DatabaseHelper extends SQLiteOpenHelper {
282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
2835524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        final String mName;
284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
285fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final boolean mEarlyUpgrade;
286fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final SQLiteDatabase.CustomFunction mObjectRemovedCallback;
2875524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        boolean mUpgradeAttempted; // Used for upgrade error handling
28810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumQueries;
28910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumUpdates;
29010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumInserts;
29110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumDeletes;
29210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStartTime;
29310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStopTime;
294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
299fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        public DatabaseHelper(Context context, String name, boolean internal,
300fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                boolean earlyUpgrade,
301fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                SQLiteDatabase.CustomFunction objectRemovedCallback) {
302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            super(context, name, null, DATABASE_VERSION);
303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
3045524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mName = name;
305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
306fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mEarlyUpgrade = earlyUpgrade;
307fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mObjectRemovedCallback = objectRemovedCallback;
308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDatabase(db, mInternal, 0, DATABASE_VERSION);
316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
3245524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = true;
325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDatabase(db, mInternal, oldV, newV);
326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
328db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        @Override
329db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        public synchronized SQLiteDatabase getWritableDatabase() {
3305524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            SQLiteDatabase result = null;
3315524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = false;
3325524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            try {
3335524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3345524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            } catch (Exception e) {
3355524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                if (!mUpgradeAttempted) {
3365524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    Log.e(TAG, "failed to open database " + mName, e);
3375524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    return null;
3385524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                }
3395524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3405524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
3415524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // If we failed to open the database during an upgrade, delete the file and try again.
3425524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // This will result in the creation of a fresh database, which will be repopulated
3435524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // when the media scanner runs.
3445524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            if (result == null && mUpgradeAttempted) {
3455524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                mContext.getDatabasePath(mName).delete();
3465524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3475524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3485524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            return result;
3495524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        }
3505524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
352993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * For devices that have removable storage, we support keeping multiple databases
353993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * to allow users to switch between a number of cards.
354993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * On such devices, touch this particular database and garbage collect old databases.
355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
36036d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky
361652c337bac290c7dfcde8725e7c771193a7f7ed6Vasu Nori            // Turn on WAL optimization
362652c337bac290c7dfcde8725e7c771193a7f7ed6Vasu Nori            db.enableWriteAheadLogging();
36336d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky
364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
366fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mEarlyUpgrade) return; // Doing early upgrade.
367fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
368fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mObjectRemovedCallback != null) {
369fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback);
370fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            }
371d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
372993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            // the code below is only needed on devices with removable storage
373993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            if (!Environment.isExternalStorageRemovable()) return;
374993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
43534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood    // synchronize on mMtpServiceConnection when accessing mMtpService
436d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private IMtpService mMtpService;
437d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
438d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final ServiceConnection mMtpServiceConnection = new ServiceConnection() {
439d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood         public void onServiceConnected(ComponentName className, android.os.IBinder service) {
44034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
44134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = IMtpService.Stub.asInterface(service);
44234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
443d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
444d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
445d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void onServiceDisconnected(ComponentName className) {
44634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
44734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = null;
44834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
449d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
450d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
451d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
452ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    private static final String[] sDefaultFolderNames = {
453ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MUSIC,
454ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PODCASTS,
455ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_RINGTONES,
456ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_ALARMS,
457ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_NOTIFICATIONS,
458ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PICTURES,
459ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MOVIES,
460ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DOWNLOADS,
461ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DCIM,
462ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    };
463ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
464ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    // creates default folders (Music, Downloads, etc)
46510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void createDefaultFolders(DatabaseHelper helper, SQLiteDatabase db) {
466ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // Use a SharedPreference to ensure we only do this once.
467ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // We don't want to annoy the user by recreating the directories
468ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // after she has deleted them.
469ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
470ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        if (prefs.getInt("created_default_folders", 0) == 0) {
471ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            for (String folderName : sDefaultFolderNames) {
472ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                File file = Environment.getExternalStoragePublicDirectory(folderName);
473ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                if (!file.exists()) {
474ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                    file.mkdirs();
47510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    insertDirectory(helper, db, file.getAbsolutePath());
476ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                }
477ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            }
478ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
479ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            SharedPreferences.Editor e = prefs.edit();
480ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.clear();
481ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.putInt("created_default_folders", 1);
482ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.commit();
483ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        }
484ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
485ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
488d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        final Context context = getContext();
489d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
490acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
491acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums._ID);
492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
494acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
496acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
501acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
502acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
503acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
504acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.ALBUM_ART);
505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
50663f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
50763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
508d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "%1", context.getString(R.string.artist_label));
509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
514d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        context.registerReceiver(mUnmountReceiver, iFilter);
515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5167d4f72d8ab71e28e9d40da87ec8c75ded254dd08Dianne Hackborn        mCaseInsensitivePaths = true;
5174f2186758ee1c6eaa702bf1511b233b26143b631Mike Lockwood
518c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        StorageManager storageManager =
519c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood                (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
520c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        mExternalStoragePaths = storageManager.getVolumePaths();
5219be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
529ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND);
530ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        ht.start();
531ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        mThumbHandler = new Handler(ht.getLooper()) {
532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
534b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
535b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
53620434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest = mMediaThumbQueue.poll();
537b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
53820434e032e498b716f87cce2f23dd646819218bfRay Chen                    if (mCurrentThumbRequest == null) {
539b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
540b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
541b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
54220434e032e498b716f87cce2f23dd646819218bfRay Chen                            File origFile = new File(mCurrentThumbRequest.mPath);
5434d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            if (origFile.exists() && origFile.length() > 0) {
54420434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.execute();
5454d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            } else {
5464d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                // original file hasn't been stored yet
5474d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                synchronized (mMediaThumbQueue) {
54820434e032e498b716f87cce2f23dd646819218bfRay Chen                                    Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath);
5494d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
5504d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
551b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
5521d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
5531d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                        } catch (UnsupportedOperationException ex) {
5541d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // This could happen if we unplug the sd card during insert/update/delete
5551d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // See getDatabaseForUri.
5561d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
55722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                        } catch (OutOfMemoryError err) {
55822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            /*
55922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Note: Catching Errors is in most cases considered
56022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * bad practice. However, in this case it is
56122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * motivated by the fact that corrupt or very large
56222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * images may cause a huge allocation to be
56322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * requested and denied. The bitmap handling API in
56422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Android offers no other way to guard against
56522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * these problems than by catching OutOfMemoryError.
56622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             */
56722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            Log.w(TAG, err);
568b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
56920434e032e498b716f87cce2f23dd646819218bfRay Chen                            synchronized (mCurrentThumbRequest) {
57020434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE;
57120434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.notifyAll();
572b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
573b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
574b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
575b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
576b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
577b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
578b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
579b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
5808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
581b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    makeThumbInternal(d);
582b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
583b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
584b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
5858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
592afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String IMAGE_COLUMNS =
593afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_size,_display_name,mime_type,title,date_added," +
594afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
595bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name," +
596bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "width,height";
597bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
598bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String IMAGE_COLUMNSv407 =
599bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_size,_display_name,mime_type,title,date_added," +
600bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
601afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name";
602afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
603805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv99 =
604afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added," +
605afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
606afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
607afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark";
608afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
609805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv100 =
610805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "_data,_display_name,_size,mime_type,date_added," +
611805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
612805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
613805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "bookmark,album_artist";
614805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen
615957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang    private static final String AUDIO_COLUMNSv405 =
616957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "_data,_display_name,_size,mime_type,date_added,is_drm," +
617957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
618957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
619957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "bookmark,album_artist";
620957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
621afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String VIDEO_COLUMNS =
622afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
623afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title,duration,artist,album,resolution,description,isprivate,tags," +
624afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
625bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width," +
626bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "height";
627bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
628bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String VIDEO_COLUMNSv407 =
629bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
630bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "title,duration,artist,album,resolution,description,isprivate,tags," +
631bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
632afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic,bucket_id,bucket_display_name, bookmark";
633afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
634afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified";
635afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDatabase(SQLiteDatabase db, boolean internal,
645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (toVersion != DATABASE_VERSION) {
649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " +
650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    DATABASE_VERSION);
651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
65395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
658d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag.
659acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        // We can't downgrade from those revisions, so start over.
660022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // (the initial change to do this was wrong, so now we actually need to start over
661022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // if the database version is 84-89)
662bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair.
663bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // However version 91 was reused in a divergent development path for gingerbread,
664bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // so we need to support upgrades from 91.
665bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Therefore we will only force a reset for versions 92 - 94.
666d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) ||
667bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    (fromVersion >= 92 && fromVersion <= 94)) {
668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
671d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            fromVersion = 63;
672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
694cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
6959ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup");
6969ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup");
6979ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup");
6989ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup");
699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
729b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
749216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                       "_data TEXT UNIQUE NOT NULL," +
750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
78995ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
795acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album) AS number_of_albums, " +
796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
809acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
810acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
811acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
812acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
813acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
83178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                // Contains mappings between audio genres and audio files
832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
922acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
923acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.0 shipped with database version 64
924acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
925acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
931403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // In version 66, originally we updateBucketNames(db, "images"),
932403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // but we need to do it in version 89 and therefore save the update here.
933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
944403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
945403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // In version 68, originally we updateBucketNames(db, "video"),
946403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // but we need to do it in version 89 and therefore save the update here.
947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
95795ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
967e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "artist='" + MediaStore.UNKNOWN_STRING + "' AND " +
968e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "album='" + MediaStore.UNKNOWN_STRING + "'" +
969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
97195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
98695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
987acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
988acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.5 shipped with database version 72
989acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
990acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
9918d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
9928d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
9938d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
9948d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
9958d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
9968d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
9978d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
9988d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
9998d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
10008d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
10018d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
10028d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
10038d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
10048d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
10058d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
1006a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1007a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
1008a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
1009a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
1010a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
1011a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
1012a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
1013a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
1014a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1015a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
1016a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1017a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
1018a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1019a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
1020a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1021a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
1022a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
1023a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
1024a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
1025a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
1026a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
1027a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
1028e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " +
1029a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1030a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1031a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
1032a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1033a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1034a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1035a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
1036a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1037a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1038a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1039a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
1040a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
1041a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
1042e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " +
1043a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1044a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
1045a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
1046a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1047a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1048a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
1049a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
1050a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1051a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1052a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1053a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
1054a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
1055a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
1056a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
1057a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
1058a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
1059a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
106059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
106159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
106295ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
106359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
106459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
106559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
106659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
106715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
106815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
106915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
107015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
107115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
107215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
107315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
107415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
107515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
107615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
107715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
107815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
107915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
108015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
1081b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1082acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1083acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.6 shipped with database version 76
1084acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1085acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1086b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
1087b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
1088b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
1089b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
1090b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
1091b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
1092b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
1093b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
1094b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
1095b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
1096b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1097b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
1098b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1099b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
1100b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
1101b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
1102b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
1103b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
11041769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen
1105acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1106acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.0 and 2.0.1 shipped with database version 77
1107acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1108acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
11091769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        if (fromVersion < 78) {
1110044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force a rescan of the video entries so we can update
11111769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            // latest changed DATE_TAKEN units (in milliseconds).
11121769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            db.execSQL("UPDATE video SET date_modified=0;");
11131769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        }
1114268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
1115acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1116acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.1 shipped with database version 78
1117acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1118acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1119268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        if (fromVersion < 79) {
1120268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // move /sdcard/albumthumbs to
1121268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // /sdcard/Android/data/com.android.providers.media/albumthumbs,
1122268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // and update the database accordingly
1123268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
11249be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String oldthumbspath = mExternalStoragePaths[0] + "/albumthumbs";
11259be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String newthumbspath = mExternalStoragePaths[0] + "/" + ALBUM_THUMB_FOLDER;
1126268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            File thumbsfolder = new File(oldthumbspath);
1127268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            if (thumbsfolder.exists()) {
1128268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                // move folder to its new location
1129268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                File newthumbsfolder = new File(newthumbspath);
1130268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                newthumbsfolder.getParentFile().mkdirs();
1131268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                if(thumbsfolder.renameTo(newthumbsfolder)) {
1132268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    // update the database
1133268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" +
1134268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                            oldthumbspath + "','" + newthumbspath + "');");
1135268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                }
1136268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            }
1137268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        }
1138044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen
1139044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        if (fromVersion < 80) {
1140044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force rescan of image entries to update DATE_TAKEN as UTC timestamp.
1141044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            db.execSQL("UPDATE images SET date_modified=0;");
1142044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        }
11430ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
1144166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen        if (fromVersion < 81 && !internal) {
11450ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete entries starting with /mnt/sdcard. This is for the benefit
11460ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // of users running builds between 2.0.1 and 2.1 final only, since
11470ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // users updating from 2.0 or earlier will not have such entries.
11480ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
11490ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // First we need to update the _data fields in the affected tables, since
11500ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // otherwise deleting the entries will also delete the underlying files
11510ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // (via a trigger), and we want to keep them.
11520ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11530ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11540ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11550ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11560ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11570ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
1158216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
11590ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Once the paths have been renamed, we can safely delete the entries
11600ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';");
11610ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM images WHERE _data IS '////';");
11620ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM video WHERE _data IS '////';");
11630ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';");
11640ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';");
11650ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_meta WHERE _data  IS '////';");
11660ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data  IS '////';");
11670ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
11680ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // rename existing entries starting with /sdcard to /mnt/sdcard
11690ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta" +
11700ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
11710ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists" +
11720ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
11730ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images" +
11740ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
11750ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video" +
11760ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
11770ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails" +
11780ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
11790ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails" +
11800ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
11810ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art" +
11820ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
11830ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
11840ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete albums and artists, then clear the modification time on songs, which
11850ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // will cause the media scanner to rescan everything, rebuilding the artist and
11860ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // album tables along the way, while preserving playlists.
11870ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // We need this rescan because ICU also changed, and now generates different
11880ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // collation keys
11890ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from albums");
11900ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from artists");
11910ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
11920ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen        }
119384403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen
119484403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        if (fromVersion < 82) {
1195acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // recreate this view with the correct "group by" specifier
119684403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("DROP VIEW IF EXISTS artist_info");
119784403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
119884403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "SELECT artist_id AS _id, artist, artist_key, " +
1199acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album_key) AS number_of_albums, " +
120084403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
120184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "GROUP BY artist_key;");
120284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        }
1203216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1204acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /* we skipped over version 83, and reverted versions 84, 85 and 86 */
1205ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen
1206ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        if (fromVersion < 87) {
1207ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // The fastscroll thumb needs an index on the strings being displayed,
1208ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // otherwise the queries it does to determine the correct position
1209ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // becomes really inefficient
1210022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);");
1211022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);");
1212022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);");
1213ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        }
1214216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1215acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        if (fromVersion < 88) {
1216acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // Clean up a few more things from versions 84/85/86, and recreate
1217acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // the few things worth keeping from those changes.
1218acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update1;");
1219acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update2;");
1220acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update3;");
1221acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update4;");
1222acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update1;");
1223acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update2;");
1224acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update3;");
1225acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update4;");
122616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP VIEW IF EXISTS album_artists;");
1227acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);");
1228acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);");
1229acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
1230acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
1231acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
1232acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
1233acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        }
1234403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1235fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // In version 89, originally we updateBucketNames(db, "images") and
1236fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames
1237fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        //  for all files and therefore can save the update here.
1238b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1239b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (fromVersion < 91) {
1240bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // Never query by mini_thumb_magic_index
1241bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index");
1242bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1243bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // sort the items by taken date in each bucket
1244bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)");
1245bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)");
1246bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        }
1247bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1248a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
1249d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // Gingerbread ended up going to version 100, but didn't yet have the "files"
1250d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // table, so we need to create that if we're at 100 or lower. This means
1251d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // we won't be able to upgrade pre-release Honeycomb.
1252d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        if (fromVersion <= 100) {
1253afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Remove various stages of work in progress for MTP support
1254afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
1255afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS files");
125616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;");
125716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;");
125816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;");
125916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;");
1260afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;");
1261afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;");
1262afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;");
1263afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;");
1264afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;");
1265afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1266afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create a new table to manage all files in our storage.
1267afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // This contains a union of all the columns from the old
1268afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // images, audio_meta, videos and audio_playlist tables.
1269afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TABLE files (" +
127000a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen                        "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1271afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data TEXT," +     // this can be null for playlists
1272afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_size INTEGER," +
1273afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "format INTEGER," +
1274afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "parent INTEGER," +
1275afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_added INTEGER," +
1276afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified INTEGER," +
1277afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mime_type TEXT," +
1278afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title TEXT," +
1279afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "description TEXT," +
1280afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_display_name TEXT," +
1281afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1282afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images
1283afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "picasa_id TEXT," +
1284afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "orientation INTEGER," +
1285afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1286afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images and video
1287afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "latitude DOUBLE," +
1288afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "longitude DOUBLE," +
1289afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken INTEGER," +
1290afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic INTEGER," +
1291afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_id TEXT," +
1292afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_display_name TEXT," +
1293afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "isprivate INTEGER," +
1294afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1295afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio
1296afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title_key TEXT," +
1297afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist_id INTEGER," +
1298afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album_id INTEGER," +
1299afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "composer TEXT," +
1300afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track INTEGER," +
1301afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "year INTEGER CHECK(year!=0)," +
1302afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_ringtone INTEGER," +
1303afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_music INTEGER," +
1304afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_alarm INTEGER," +
1305afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_notification INTEGER," +
1306afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_podcast INTEGER," +
1307d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        "album_artist TEXT," +
1308afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1309afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio and video
1310afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "duration INTEGER," +
1311afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark INTEGER," +
1312afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1313afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for video
1314afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist TEXT," +
1315afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album TEXT," +
1316afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "resolution TEXT," +
1317afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "tags TEXT," +
1318afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category TEXT," +
1319afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "language TEXT," +
1320afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_data TEXT," +
1321afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for playlists
1323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "name TEXT," +
1324afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1325afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // media_type is used by the views to emulate the old
1326afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // images, audio_meta, videos and audio_playlist tables.
1327afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "media_type INTEGER," +
1328afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1329afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Value of _id from the old media table.
1330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Used only for updating other tables during database upgrade.
1331afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "old_id INTEGER" +
1332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       ");");
1333d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen
1334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX path_index ON files(_data);");
1335afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Copy all data from our obsolete tables to the new files table
133892be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
133992be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Copy audio records first, preserving the _id column.
134092be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We do this to maintain compatibility for content Uris for ringtones.
134192be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Unfortunately we cannot do this for images and videos as well.
134292be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We choose to do this for the audio table because the fragility of Uris
134392be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // for ringtones are the most common problem we need to avoid.
134492be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            db.execSQL("INSERT INTO files (_id," + AUDIO_COLUMNSv99 + ",old_id,media_type)" +
134592be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " SELECT _id," + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO +
134692be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " FROM audio_meta;");
134792be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
1348bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + IMAGE_COLUMNSv407 + ",old_id,media_type) SELECT "
1349bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + IMAGE_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;");
1350bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + VIDEO_COLUMNSv407 + ",old_id,media_type) SELECT "
1351bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + VIDEO_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;");
135216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            if (!internal) {
1353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT "
1354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST
1355afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + " FROM audio_playlists;");
1356afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
135716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1358afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Delete the old tables
1359afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS images");
1360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_meta");
1361afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS video");
1362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
136316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create views to replace our old tables
1365bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNSv407 +
1366afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1367afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1368d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 +
1369d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1370d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1371bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNSv407 +
1372afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1374afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1375afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS +
1376afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1377afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_PLAYLIST + ";");
137816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            }
137936339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
13809491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // create temporary index to make the updates go faster
13819491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("CREATE INDEX tmp ON files(old_id);");
13829491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update the image_id column in the thumbnails table.
1384afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files "
1385afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = thumbnails.image_id AND files.media_type = "
1386afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ");");
138736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1388afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1389d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // update audio_id in the audio_genres_map table, and
1390d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // audio_playlists_map tables and playlist_id in the audio_playlists_map table
1391afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files "
1392afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = "
1393afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_AUDIO + ");");
1394afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files "
1395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_playlists_map.audio_id "
1396afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");");
1397d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET playlist_id = (SELECT _id FROM files "
1398d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "WHERE files.old_id = audio_playlists_map.playlist_id "
1399d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + ");");
1400afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
1401afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1402afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update video_id in the videothumbnails table.
1403afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files "
1404afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = "
1405afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ");");
1406afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
14079491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // we don't need this index anymore now
14089491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("DROP INDEX tmp;");
14099491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1410afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update indices to work on the files table
1411afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS title_idx");
1412afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS album_id_idx");
1413afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS image_bucket_index");
1414afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS video_bucket_index");
1415afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS sort_index");
1416afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS titlekey_index");
1417afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS artist_id_idx");
1418afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX title_idx ON files(title);");
1419afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1420afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);");
1421afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1422afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1424afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1425afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Recreate triggers for our obsolete tables on the new files table
1426afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
1427afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
1428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
1429afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
1430afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
143136339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1432afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " +
1433afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " +
143436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1435afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
1436afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
143736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
143836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1439afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " +
144049dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " +
144136339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1442afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
144336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
144436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1445afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1446afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " +
1447afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " +
1448afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1449afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
1450afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
1451afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " +
1454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " +
1455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1456afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1457afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "SELECT _DELETE_FILE(old._data);" +
1458afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1459afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1460afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
146136339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "BEGIN " +
1462afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from files where _id=old._id;" +
1463afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_playlists_map where audio_id=old._id;" +
1464afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_genres_map where audio_id=old._id;" +
146536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "END");
146636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood            }
146736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood        }
146836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1469fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        if (fromVersion < 300) {
1470fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            // we now compute bucket and display names for all files to avoid problems with files
1471fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            // that the media scanner might not recognize as images or videos
1472fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            updateBucketNames(db, "files");
1473fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        }
1474fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood
1475db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (fromVersion < 301) {
1476db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("DROP INDEX IF EXISTS bucket_index");
1477db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)");
1478db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)");
1479db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
1480db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin
148120405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        if (fromVersion < 302) {
148220405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX parent_index ON files(parent);");
148320405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX format_index ON files(format);");
148420405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        }
148520405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood
14862658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        if (fromVersion < 303) {
14872658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // the album disambiguator hash changed, so rescan songs and force
14882658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // albums to be updated. Artists are unaffected.
14892658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("DELETE from albums");
14902658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
14912658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
14922658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        }
14932658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
14944b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 304 && !internal) {
149551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            // notifies host when files are deleted
149651d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
149751d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "BEGIN " +
149851d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                        "SELECT _OBJECT_REMOVED(old._id);" +
149951d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "END");
150051d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
150151d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood        }
150251d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
15034b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 305 && internal) {
15044b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            // version 304 erroneously added this trigger to the internal database
15054b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
15064b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        }
15074b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood
1508fda522dc66b94057f9c6676cb8ba10bc3b13daeaMarco Nelissen        if (fromVersion < 306 && !internal) {
1509efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // The genre list was expanded and genre string parsing was tweaked, so
1510efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // rebuild the genre list
1511efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
1512efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
1513efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
1514efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
1515efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen        }
1516efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen
151765587f9c204abb2d179527dfdca009f4780e9743Ray Chen        if (fromVersion < 307 && !internal) {
151865587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // Force rescan of image entries to update DATE_TAKEN by either GPSTimeStamp or
151965587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // EXIF local time.
152065587f9c204abb2d179527dfdca009f4780e9743Ray Chen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
152165587f9c204abb2d179527dfdca009f4780e9743Ray Chen                    + FileColumns.MEDIA_TYPE_IMAGE + ";");
152265587f9c204abb2d179527dfdca009f4780e9743Ray Chen        }
152365587f9c204abb2d179527dfdca009f4780e9743Ray Chen
15244f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // Database version 401 did not add storage_id to the internal database.
15254f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // We need it there too, so add it in version 402
15264f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        if (fromVersion < 401 || (fromVersion == 401 && internal)) {
15279be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Add column for MTP storage ID
15289be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("ALTER TABLE files ADD COLUMN storage_id INTEGER;");
15299be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Anything in the database before this upgrade step will be in the primary storage
15309be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("UPDATE files SET storage_id=" + MtpStorage.getStorageId(0) + ";");
15319be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
15329be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
153378b2885edc406273d688536b0eadfea006b20662Marco Nelissen        if (fromVersion < 403 && !internal) {
153478b2885edc406273d688536b0eadfea006b20662Marco Nelissen            db.execSQL("CREATE VIEW audio_genres_map_noid AS " +
153578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    "SELECT audio_id,genre_id from audio_genres_map;");
153678b2885edc406273d688536b0eadfea006b20662Marco Nelissen        }
153778b2885edc406273d688536b0eadfea006b20662Marco Nelissen
15389289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        if (fromVersion < 404) {
15399289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // There was a bug that could cause distinct same-named albums to be
15409289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // combined again. Delete albums and force a rescan.
15419289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("DELETE from albums");
15429289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
15439289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
15449289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        }
15459289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen
1546957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        if (fromVersion < 405) {
1547957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            // Add is_drm column.
1548957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("ALTER TABLE files ADD COLUMN is_drm INTEGER;");
1549957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1550957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("DROP VIEW IF EXISTS audio_meta");
1551957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv405 +
1552957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1553957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1554957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1555957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            recreateAudioView(db);
1556957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        }
1557957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1558b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (fromVersion < 407) {
15597ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            // Rescan files in the media database because a new column has been added
1560b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // in table files in version 405 and to recover from problems populating
1561b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // the genre tables
15627ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            db.execSQL("UPDATE files SET date_modified=0;");
15637ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang        }
15647ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang
1565bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        if (fromVersion < 408) {
1566bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Add the width/height columns for images and video
1567bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN width INTEGER;");
1568bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN height INTEGER;");
1569bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1570bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Rescan files to fill the columns
1571bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("UPDATE files SET date_modified=0;");
1572bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1573bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Update images and video views to contain the width/height columns
1574bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS images");
1575bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS video");
1576bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS +
1577bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1578bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1579bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS +
1580bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1581bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1582bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        }
1583bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1584acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sanityCheck(db, fromVersion);
15851d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    }
15861d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen
15871d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    /**
1588216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * Perform a simple sanity check on the database. Currently this tests
1589216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * whether all the _data entries in audio_meta are unique
1590216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     */
1591216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen    private static void sanityCheck(SQLiteDatabase db, int fromVersion) {
1592216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c1 = db.query("audio_meta", new String[] {"count(*)"},
1593216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1594216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"},
1595216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1596216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.moveToFirst();
1597216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.moveToFirst();
1598216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num1 = c1.getInt(0);
1599216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num2 = c2.getInt(0);
1600216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.close();
1601216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.close();
1602216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        if (num1 != num2) {
1603216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            Log.e(TAG, "audio_meta._data column is not unique while upgrading" +
1604216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                    " from schema " +fromVersion + " : " + num1 +"/" + num2);
1605216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            // Delete all audio_meta rows so they will be rebuilt by the media scanner
1606216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("DELETE FROM audio_meta;");
1607216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        }
1608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
1611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
1612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note that views are read-only, so we define a trigger to allow deletes.
1613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
1614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
1615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
1616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
1617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
1618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
1620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                "BEGIN " +
1621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_meta where _id=old._id;" +
1622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_playlists_map where audio_id=old._id;" +
1623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_genres_map where audio_id=old._id;" +
1624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                "END");
1625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
162695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the bucket_id and
1629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * bucket_display_name columns are correct.
1630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateBucketNames(SQLiteDatabase db, String tableName) {
1634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
1635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
1638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
16429491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                String [] rowId = new String[1];
1643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
16459491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                    rowId[0] = String.valueOf(cursor.getInt(idColumnIndex));
1646d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    if (data != null) {
1647d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        ContentValues values = new ContentValues();
1648d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        computeBucketValues(data, values);
16499491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                        db.update(tableName, values, "_id=?", rowId);
1650d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    } else {
1651d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        Log.w(TAG, "null data at id " + rowId);
1652d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    }
1653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
1665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
1666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
1670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
1671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
1674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
1678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
1679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
1680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
1682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
1683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
1684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
1685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
1686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
1687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
1688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
1701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
1705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
1706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
1707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
1708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
1711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
1712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
1713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
1714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
1715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
1717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
1718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
1719d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood        values.put(ImageColumns.BUCKET_ID, path.hashCode());
1720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
1721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
1726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
1729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
1730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
1731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
1732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
1733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
1735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1737b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
1738498b62c2912302a23532c73a028a7684c5df33caRay Chen     * Copy taken time from date_modified if we lost the original value (e.g. after factory reset)
1739498b62c2912302a23532c73a028a7684c5df33caRay Chen     * This works for both video and image tables.
1740b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     *
1741b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     * @param values the content values, where taken time is updated.
1742b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     */
1743b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    private static void computeTakenTime(ContentValues values) {
1744b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        if (! values.containsKey(Images.Media.DATE_TAKEN)) {
1745b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // This only happens when MediaScanner finds an image file that doesn't have any useful
1746b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // reference to get this value. (e.g. GPSTimeStamp)
1747498b62c2912302a23532c73a028a7684c5df33caRay Chen            Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED);
1748b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            if (lastModified != null) {
1749b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                values.put(Images.Media.DATE_TAKEN, lastModified * 1000);
1750b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            }
1751b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        }
1752b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    }
1753b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen
1754b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    /**
1755b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
1756b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
1757b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
1758b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
1759b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
1760b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
1761b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
1762b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
1763b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c == null) return false;
1764b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1765b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
1766b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1767b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c.moveToFirst()) {
1768e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen            long id = c.getLong(0);
1769b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String path = c.getString(1);
1770b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            long magic = c.getLong(2);
1771b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
17729299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            MediaThumbRequest req = requestMediaThumbnail(path, origUri,
17739299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    MediaThumbRequest.PRIORITY_HIGH, magic);
17749299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            if (req == null) {
17759299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                return false;
17769299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            }
17779299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            synchronized (req) {
17789299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                try {
17799299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    while (req.mState == MediaThumbRequest.State.WAIT) {
17809299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        req.wait();
178120434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
17829299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                } catch (InterruptedException e) {
17839299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    Log.w(TAG, e);
17849299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                }
17859299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                if (req.mState == MediaThumbRequest.State.DONE) {
17869299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    result = true;
1787b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1788b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
1789b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1790b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        c.close();
1791b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1792b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
1793b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1794b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1795e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid,
1796e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo) {
1797e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllOrigId = (id == -1);
1798e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllGroupId = (gid == -1);
1799e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        return (req.mCallingPid == pid) &&
1800e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllGroupId || req.mGroupId == gid) &&
1801e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllOrigId || req.mOrigId == id) &&
1802e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (req.mIsVideo == isVideo);
1803e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    }
1804e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
1805b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
1806b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
1807b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
1808b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
1809b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
1810b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
1811b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
1812b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
1813b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1814b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1815b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
1816b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
1817b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
1818b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
1819b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1820b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1821b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1822b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
182320434e032e498b716f87cce2f23dd646819218bfRay Chen        boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel"));
1824e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        Uri origUri = uri.buildUpon().encodedPath(
1825e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                uri.getPath().replaceFirst("thumbnails", "media"))
1826e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                .appendPath(origId).build();
1827b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1828b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
182920434e032e498b716f87cce2f23dd646819218bfRay Chen            Log.w(TAG, "original media doesn't exist or it's canceled.");
1830b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
183120434e032e498b716f87cce2f23dd646819218bfRay Chen        } else if (cancelRequest) {
1832e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            String groupId = uri.getQueryParameter("group_id");
1833e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo = "video".equals(uri.getPathSegments().get(1));
183420434e032e498b716f87cce2f23dd646819218bfRay Chen            int pid = Binder.getCallingPid();
183520434e032e498b716f87cce2f23dd646819218bfRay Chen            long id = -1;
1836e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            long gid = -1;
1837e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
183820434e032e498b716f87cce2f23dd646819218bfRay Chen            try {
183920434e032e498b716f87cce2f23dd646819218bfRay Chen                id = Long.parseLong(origId);
1840e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                gid = Long.parseLong(groupId);
184120434e032e498b716f87cce2f23dd646819218bfRay Chen            } catch (NumberFormatException ex) {
184220434e032e498b716f87cce2f23dd646819218bfRay Chen                // invalid cancel request
184320434e032e498b716f87cce2f23dd646819218bfRay Chen                return false;
184420434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1845e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
184620434e032e498b716f87cce2f23dd646819218bfRay Chen            synchronized (mMediaThumbQueue) {
1847e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                if (mCurrentThumbRequest != null &&
1848e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                        matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) {
184920434e032e498b716f87cce2f23dd646819218bfRay Chen                    synchronized (mCurrentThumbRequest) {
185020434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL;
185120434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.notifyAll();
185220434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
185320434e032e498b716f87cce2f23dd646819218bfRay Chen                }
185420434e032e498b716f87cce2f23dd646819218bfRay Chen                for (MediaThumbRequest mtq : mMediaThumbQueue) {
1855e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                    if (matchThumbRequest(mtq, pid, id, gid, isVideo)) {
185620434e032e498b716f87cce2f23dd646819218bfRay Chen                        synchronized (mtq) {
185720434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.mState = MediaThumbRequest.State.CANCEL;
185820434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.notifyAll();
185920434e032e498b716f87cce2f23dd646819218bfRay Chen                        }
186020434e032e498b716f87cce2f23dd646819218bfRay Chen
186120434e032e498b716f87cce2f23dd646819218bfRay Chen                        mMediaThumbQueue.remove(mtq);
186220434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
186320434e032e498b716f87cce2f23dd646819218bfRay Chen                }
186420434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1865b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1866b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1867b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
1868b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
1869b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1870b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
1871b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1872b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
1873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
1875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
1876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
1877baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        List<String> prependArgs = new ArrayList<String>();
1878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
187901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
1880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
1881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
1882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
1883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
1884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
1885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
18860027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
18870027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                c.addRow(new String[] {mMediaScannerVolume});
18880027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                return c;
1889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
18920027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // Used temporarily (until we have unique media IDs) to get an identifier
18930027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // for the current sd card, so that the music app doesn't have to use the
18940027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // non-public getFatVolumeId method
18950027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        if (table == FS_ID) {
18960027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"fsid"});
18970027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            c.addRow(new Integer[] {mVolumeId});
18980027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            return c;
18990027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        }
19000027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
1901704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        if (table == VERSION) {
1902704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"version"});
1903704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            c.addRow(new Integer[] {DATABASE_VERSION});
1904704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            return c;
1905704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        }
1906704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen
1907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
190810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
190910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
1910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
1911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
191210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
191310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getReadableDatabase();
19145fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) return null;
1915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
19164574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
1917c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String filter = uri.getQueryParameter("filter");
1918c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String [] keywords = null;
1919c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        if (filter != null) {
1920c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            filter = Uri.decode(filter).trim();
1921c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            if (!TextUtils.isEmpty(filter)) {
1922c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                String [] searchWords = filter.split(" ");
1923c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                keywords = new String[searchWords.length];
1924c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                Collator col = Collator.getInstance();
1925c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                col.setStrength(Collator.PRIMARY);
1926c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; i < searchWords.length; i++) {
1927c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    String key = MediaStore.Audio.keyFor(searchWords[i]);
1928c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("\\", "\\\\");
1929c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("%", "\\%");
1930c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("_", "\\_");
1931c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    keywords[i] = key;
1932c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
1933c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            }
1934c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        }
1935db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (uri.getQueryParameter("distinct") != null) {
1936db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            qb.setDistinct(true);
1937db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
1938c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen
1939b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
1942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
1943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
1948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
1949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
1954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
1955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
1957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
1958baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
1959baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
1960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
1963b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
1964b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
1965b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
1966b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
1967b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1971d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
1972ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.equalsIgnoreCase("is_music=1")
1973ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                          || selection.equalsIgnoreCase("is_podcast=1") )
1974c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
1975c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
1976ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting songs");
1977ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
1978ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
1979ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio");
1980c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
1981c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
1982c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
1983c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
1984c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
1985c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
1986baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'");
1987baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
1988c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
1989ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
1990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1994baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
1995baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
1996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
2001baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_genres_map WHERE audio_id=?)");
2002baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2007baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2008baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
2014baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_playlists_map WHERE audio_id=?)");
2015baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2020baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2021baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2030baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2031baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2034bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen            case AUDIO_GENRES_ALL_MEMBERS:
2035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
203678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                {
203778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // if simpleQuery is true, we can do a simpler query on just audio_genres_map
203878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // we can do this if we have no keywords and our projection includes just columns
203978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // from audio_genres_map
204078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    boolean simpleQuery = (keywords == null && projectionIn != null
204178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            && (selection == null || selection.equalsIgnoreCase("genre_id=?")));
204278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (projectionIn != null) {
204378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; i < projectionIn.length; i++) {
204478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            String p = projectionIn[i];
204578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (p.equals("_id")) {
204678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // note, this is different from playlist below, because
204778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // "_id" used to (wrongly) be the audio id in this query, not
204878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // the row id of the entry in the map, and we preserve this
204978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // behavior for backwards compatibility
205078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
205178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
205278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (simpleQuery && !(p.equals("audio_id") ||
205378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    p.equals("genre_id"))) {
205478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
205578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
205678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
205778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
205878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (simpleQuery) {
205978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid");
2060bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2061baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere("genre_id=?");
2062baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2063bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
206478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    } else {
206578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid, audio");
2066bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        qb.appendWhere("audio._id = audio_id");
2067bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2068baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere(" AND genre_id=?");
2069baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2070bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
207178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; keywords != null && i < keywords.length; i++) {
207278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(" AND ");
207378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
207478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.ALBUM_KEY +
207578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.TITLE_KEY +
2076baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                    " LIKE ? ESCAPE '\\'");
2077baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add("%" + keywords[i] + "%");
207878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
207978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
208078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                }
2081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2087702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2089baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2090baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2093b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2095e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // if simpleQuery is true, we can do a simpler query on just audio_playlists_map
2096e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // we can do this if we have no keywords and our projection includes just columns
2097e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // from audio_playlists_map
20984382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                boolean simpleQuery = (keywords == null && projectionIn != null
20994382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                        && (selection == null || selection.equalsIgnoreCase("playlist_id=?")));
210097e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
210197e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
2102e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        String p = projectionIn[i];
2103e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (simpleQuery && !(p.equals("audio_id") ||
2104e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                p.equals("playlist_id") || p.equals("play_order"))) {
2105e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                            simpleQuery = false;
2106e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        }
2107e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (p.equals("_id")) {
210897e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
210997e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
2110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2112e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                if (simpleQuery) {
2113e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map");
2114baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("playlist_id=?");
2115baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2116e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                } else {
2117e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map, audio");
2118baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("audio._id = audio_id AND playlist_id=?");
2119baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2120e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2121e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(" AND ");
2122e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2123e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2124e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.TITLE_KEY +
2125baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2126baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2127e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    }
2128c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2129b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                if (table == AUDIO_PLAYLISTS_ID_MEMBERS_ID) {
2130baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere(" AND audio_playlists_map._id=?");
2131baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(5));
2132b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                }
2133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2140baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2141baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2144b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
2145b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2146b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
2147b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
2148b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2149b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2150b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2151b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
2153d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2154ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2155c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2156c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2157ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting artists");
2158ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2159ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct artist_id)";
2160ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2161ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2162ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("artist_info");
2163c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2164c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2165c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2166c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2167c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2168baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2169baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2170c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2171ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
2175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
2176baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2177baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
2181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
2182acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
2183acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        " audio.album_id=album_art.album_id");
2184acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
2185baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "artists_albums_map WHERE artist_id=?)");
2186baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(aid);
2187c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; keywords != null && i < keywords.length; i++) {
2188c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(" AND ");
2189c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2190c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            "||" + MediaStore.Audio.Media.ALBUM_KEY +
2191baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            " LIKE ? ESCAPE '\\'");
2192baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add("%" + keywords[i] + "%");
2193c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2194acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                groupBy = "audio.album_id";
2195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
2196acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
2197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
2198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
2199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
2202d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2203ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2204c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2205c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2206ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting albums");
2207ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2208ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct album_id)";
2209ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2210ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2211ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("album_info");
2212c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2213c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2214c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2215c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2216c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2217c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2218baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2219baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2220c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2221ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
2225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
2226baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2227baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
2231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
2232baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("album_id=?");
2233baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2236a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
2237a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
2238a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
2239a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
2240a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
2241baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection,
2242baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        combine(prependArgs, selectionArgs), sort, table, limit);
2243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
224416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
2245e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
2246baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2247baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(2));
2248b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                // fall through
224916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
2250e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
225116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                qb.setTables("files");
2252b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                break;
2253b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2254e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            case MTP_OBJECT_REFERENCES:
2255e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int handle = Integer.parseInt(uri.getPathSegments().get(2));
225610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                return getObjectReferences(helper, db, handle);
2257e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
2259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
2260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2262baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection,
2263baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        //        combine(prependArgs, selectionArgs), groupBy, null, sort, limit));
2264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
2265baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
2266b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
2268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            c.setNotificationUri(getContext().getContentResolver(), uri);
2269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2270b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
2272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2274baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    private String[] combine(List<String> prepend, String[] userArgs) {
2275baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int presize = prepend.size();
2276baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        if (presize == 0) {
2277baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            return userArgs;
2278baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2279baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2280baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int usersize = (userArgs != null) ? userArgs.length : 0;
2281baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        String [] combined = new String[presize + usersize];
2282baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < presize; i++) {
2283baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[i] = prepend.get(i);
2284baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2285baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < usersize; i++) {
2286baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[presize + i] = userArgs[i];
2287baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2288baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        return combined;
2289baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    }
2290baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
2292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
22934574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
22944574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
2295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
229618c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen        String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment();
2297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
2298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
2300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
2301a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
2302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Collator col = Collator.getInstance();
2303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        col.setStrength(Collator.PRIMARY);
2304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
2305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
2306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
2307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
23083001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            String key = MediaStore.Audio.keyFor(searchWords[i]);
23093001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("\\", "\\\\");
23103001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("%", "\\%");
23113001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("_", "\\_");
2312a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
2313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
23143001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                        searchWords[i].equals("the")) ? "%" : "%" + key + "%";
2315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2317a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
2318a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
2319a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
23203001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where = "match LIKE ? ESCAPE '\\'";
2321a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
23223001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where += " AND match LIKE ? ESCAPE '\\'";
2323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2326a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
2327a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
2328a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
2329a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
2330a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
2331a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
2332a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
2333a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
2334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
23354574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
2336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
2340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
2342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2346c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case FILES_ID:
234726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                Cursor c = null;
234826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                try {
234926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    c = query(url, MIME_TYPE_PROJECTION, null, null, null);
235026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null && c.getCount() == 1) {
235126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.moveToFirst();
235226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        String mimeType = c.getString(1);
235326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.deactivate();
235426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        return mimeType;
235526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
235626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                } finally {
235726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null) {
235826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.close();
235926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
2360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
2365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
2366804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen            case AUDIO_ALBUMART_ID:
2367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
2369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
2372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
2374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
2378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
2381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
2384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
2387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
2390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2391804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen        throw new IllegalStateException("Unknown URL : " + url);
2392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
2396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * present a new file is created.
2397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
2399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
2400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
2402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
2403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
2404801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        String file = initialValues.getAsString(MediaStore.MediaColumns.DATA);
2405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
2406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
2407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
2408801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            values.put(MediaStore.MediaColumns.DATA, file);
2409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
2411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!ensureFileExists(file)) {
2414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalStateException("Unable to create new file: " + file);
2415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
2417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2419d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectAdded(long objectHandle) {
242034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
242134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
242234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
242334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectAdded((int)objectHandle);
242434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
242534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectAdded", e);
242634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
242734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2428d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2429d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2430d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2431d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2432d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectRemoved(long objectHandle) {
243334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
243434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
243534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
243634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectRemoved((int)objectHandle);
243734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
243834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectRemoved", e);
243934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
244034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2441d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2442d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2443d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2444d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
2447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
2449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
2450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
245110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
245210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
245610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
24575fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) {
24585fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            throw new IllegalStateException("Couldn't open database for " + uri);
24595fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        }
2460ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2461ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
2462ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            return playlistBulkInsert(db, uri, values);
2463e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } else if (match == MTP_OBJECT_REFERENCES) {
2464e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            int handle = Integer.parseInt(uri.getPathSegments().get(2));
246510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            return setObjectReferences(helper, db, handle, values);
2466ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2467ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
2469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
2470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
2472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
2473801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                if (values[i] != null) {
2474801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                    insertInternal(uri, match, values[i]);
2475801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                }
2476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
2478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
2479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
2481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
2483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
2484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2487801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood    public Uri insert(Uri uri, ContentValues initialValues) {
24885d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        int match = URI_MATCHER.match(uri);
24895d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        Uri newUri = insertInternal(uri, match, initialValues);
24905d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // do not signal notification for MTP objects.
24915d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // we will signal instead after file transfer is successful.
2492e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        if (newUri != null && match != MTP_OBJECTS) {
2493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
2494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
2496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2498ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) {
2499ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        DatabaseUtils.InsertHelper helper =
2500ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            new DatabaseUtils.InsertHelper(db, "audio_playlists_map");
25018b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
25028b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID);
25038b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
25048b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        long playlistId = Long.parseLong(uri.getPathSegments().get(3));
2505ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2506ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        db.beginTransaction();
2507ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        int numInserted = 0;
2508ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        try {
2509ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            int len = values.length;
2510ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            for (int i = 0; i < len; i++) {
25118b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.prepareForInsert();
25128b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // getting the raw Object and converting it long ourselves saves
25138b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // an allocation (the alternative is ContentValues.getAsLong, which
25148b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // returns a Long object)
25158b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                long audioid = ((Number) values[i].get(
25168b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue();
25178b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(audioidcolidx, audioid);
25188b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playlistididx, playlistId);
25198b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // convert to int ourselves to save an allocation.
25208b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                int playorder = ((Number) values[i].get(
25218b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue();
25228b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playorderidx, playorder);
25238b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.execute();
2524ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            }
2525ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            numInserted = len;
2526ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.setTransactionSuccessful();
2527ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        } finally {
2528ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.endTransaction();
2529ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            helper.close();
2530ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2531ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        getContext().getContentResolver().notifyChange(uri, null);
2532ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        return numInserted;
2533ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    }
2534ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
253510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertDirectory(DatabaseHelper helper, SQLiteDatabase db, String path) {
253610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (LOCAL_LOGV) Log.v(TAG, "inserting directory " + path);
2537ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        ContentValues values = new ContentValues();
2538ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION);
2539ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.DATA, path);
254010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        values.put(FileColumns.PARENT, getParent(helper, db, path));
25419be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        values.put(FileColumns.STORAGE_ID, getStorageId(path));
2542f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        File file = new File(path);
2543f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        if (file.exists()) {
2544f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
2545f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        }
254610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumInserts++;
2547ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2548ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        sendObjectAdded(rowId);
2549ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        return rowId;
2550ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
2551ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
255210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getParent(DatabaseHelper helper, SQLiteDatabase db, String path) {
2553b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        int lastSlash = path.lastIndexOf('/');
2554b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (lastSlash > 0) {
2555b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String parentPath = path.substring(0, lastSlash);
25569be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            for (int i = 0; i < mExternalStoragePaths.length; i++) {
25579be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (parentPath.equals(mExternalStoragePaths[i])) {
25589be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return 0;
25599be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
2560b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
25617f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Long cid = mDirectoryCache.get(parentPath);
25627f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            if (cid != null) {
25637f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath);
25647f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return cid;
25657f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            }
25667f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
2567f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood            // Use "LIKE" instead of "=" on case insensitive file systems so we do a
2568f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood            // case insensitive match when looking for parent directory.
25697f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: investigate whether a "nocase" constraint on the column and
25707f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // using "=" would give the same result faster.
2571f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood            String selection = (mCaseInsensitivePaths ? MediaStore.MediaColumns.DATA + " LIKE ?"
2572a9bb89771934314157dd26253195dc16bddc2cfaDongwon Kang                    // search only directories.
257310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    + " AND format=" + MtpConstants.FORMAT_ASSOCIATION
2574f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood                    : MediaStore.MediaColumns.DATA + "=?");
2575b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String [] selargs = { parentPath };
257610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
25777f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
2578b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            try {
25797f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                long id;
2580b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c == null || c.getCount() == 0) {
2581b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    // parent isn't in the database - so add it
258210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    id = insertDirectory(helper, db, parentPath);
25837f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath);
2584b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                } else {
2585b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    c.moveToFirst();
25867f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    id = c.getLong(0);
25877f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath);
2588b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                }
25897f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.put(parentPath, id);
25907f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return id;
2591b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            } finally {
2592b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c != null) c.close();
2593b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
2594b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        } else {
2595b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            return 0;
2596b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        }
2597b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    }
2598b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
25999be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private int getStorageId(String path) {
26009be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        for (int i = 0; i < mExternalStoragePaths.length; i++) {
26019be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String test = mExternalStoragePaths[i];
26029be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (path.startsWith(test)) {
26039be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int length = test.length();
26049be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (path.length() == length || path.charAt(length) == '/') {
26059be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return MtpStorage.getStorageId(i);
26069be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
26079be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
26089be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
26099be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        // default to primary storage
26109be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        return MtpStorage.getStorageId(0);
26119be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    }
26129be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
261310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, int mediaType,
2614afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            boolean notify) {
261510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
2616afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        ContentValues values = null;
2617afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2618afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        switch (mediaType) {
2619afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_IMAGE: {
262010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".jpg", "DCIM/Camera");
2621afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2622afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2623afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String data = values.getAsString(MediaColumns.DATA);
2624afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
2625afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    computeDisplayName(data, values);
2626afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2627afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2628afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2629afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2630afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2631afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_AUDIO: {
2632afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // SQLite Views are read-only, so we need to deconstruct this
2633afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // insert and do inserts into the underlying tables.
2634afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // If doing this here turns out to be a performance bottleneck,
2635afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // consider moving this to native code and using triggers on
2636afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the view.
2637afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values = new ContentValues(initialValues);
2638afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
26392658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
26402658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
26412658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                values.remove(MediaStore.Audio.Media.COMPILATION);
26422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
2643afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Insert the artist into the artist table and remove it from
2644afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the input values
2645afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                Object so = values.get("artist");
2646afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String s = (so == null ? "" : so.toString());
2647afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("artist");
2648afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long artistRowId;
264910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> artistCache = helper.mArtistCache;
2650801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String path = values.getAsString(MediaStore.MediaColumns.DATA);
2651afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(artistCache) {
2652afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = artistCache.get(s);
2653afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
265410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        artistRowId = getKeyIdForName(helper, db,
265510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "artists", "artist_key", "artist",
2656afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, s, path, 0, null, artistCache, uri);
2657afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
2658afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        artistRowId = temp.longValue();
2659afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
2660afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2661afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String artist = s;
2662afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2663afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Do the same for the album field
2664afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.get("album");
2665afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
2666afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("album");
2667afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long albumRowId;
266810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> albumCache = helper.mAlbumCache;
2669afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(albumCache) {
26702658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    int albumhash = 0;
26712658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    if (albumartist != null) {
26722658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = albumartist.hashCode();
26732658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else if (compilation != null && compilation.equals("1")) {
26742658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        // nothing to do, hash already set
26752658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else {
26762658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
26772658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    }
2678afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    String cacheName = s + albumhash;
2679afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = albumCache.get(cacheName);
2680afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
268110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        albumRowId = getKeyIdForName(helper, db,
268210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "albums", "album_key", "album",
2683afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, cacheName, path, albumhash, artist, albumCache, uri);
2684afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
2685afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        albumRowId = temp;
2686afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
2687afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
26885d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
2689afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("artist_id", Integer.toString((int)artistRowId));
2690afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("album_id", Integer.toString((int)albumRowId));
2691afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.getAsString("title");
2692afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
2693afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title_key", MediaStore.Audio.keyFor(s));
2694afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // do a final trim of the title, in case it started with the special
2695afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // "sort first" character (ascii \001)
2696afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("title");
2697afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title", s.trim());
2698b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2699801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                computeDisplayName(values.getAsString(MediaStore.MediaColumns.DATA), values);
2700afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2701afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2702afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2703afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_VIDEO: {
270410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".3gp", "video");
2705801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String data = values.getAsString(MediaStore.MediaColumns.DATA);
2706afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeDisplayName(data, values);
2707afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2708afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2709afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2710afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2711afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2712afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (values == null) {
2713afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(initialValues);
2714afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2715fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // compute bucket_id and bucket_display_name for all files
2716afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String path = values.getAsString(MediaStore.MediaColumns.DATA);
2717fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        if (path != null) {
2718fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            computeBucketValues(path, values);
2719fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        }
2720fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2721afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2722afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        long rowId = 0;
2723afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer i = values.getAsInteger(
2724afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
2725afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (i != null) {
2726afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = i.intValue();
2727afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(values);
2728afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
2729afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
2730afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2731afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String title = values.getAsString(MediaStore.MediaColumns.TITLE);
27323572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen        if (title == null && path != null) {
2733c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            title = MediaFile.getFileTitle(path);
2734c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2735c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        values.put(FileColumns.TITLE, title);
2736c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2737afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE);
2738afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer formatObject = values.getAsInteger(FileColumns.FORMAT);
2739c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        int format = (formatObject == null ? 0 : formatObject.intValue());
2740c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format == 0) {
274163b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang            if (TextUtils.isEmpty(path)) {
2742c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                // special case device created playlists
2743afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
2744c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST);
2745c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    // create a file path for the benefit of MTP
27469be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    path = mExternalStoragePaths[0]
2747afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            + "/Playlists/" + values.getAsString(Audio.Playlists.NAME);
2748c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(MediaStore.MediaColumns.DATA, path);
274910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    values.put(FileColumns.PARENT, getParent(helper, db, path));
2750c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                } else {
275163b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang                    Log.e(TAG, "path is empty in insertFile()");
2752c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                }
2753c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            } else {
2754c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                format = MediaFile.getFormatCode(path, mimeType);
2755c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
2756c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2757c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format != 0) {
2758c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.FORMAT, format);
2759c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            if (mimeType == null) {
2760c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                mimeType = MediaFile.getMimeTypeForFormatCode(format);
2761c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
2762c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2763c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2764801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        if (mimeType == null && path != null) {
2765c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            mimeType = MediaFile.getMimeTypeForFile(path);
2766c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2767c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (mimeType != null) {
2768c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.MIME_TYPE, mimeType);
2769afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2770f1f6a9e343033de33fc748f659b9221f8d5b06e1Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_NONE && !MediaScanner.isNoMediaPath(path)) {
2771afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int fileType = MediaFile.getFileTypeForMimeType(mimeType);
2772afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (MediaFile.isAudioFileType(fileType)) {
2773afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_AUDIO;
2774afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isVideoFileType(fileType)) {
2775afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_VIDEO;
2776afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isImageFileType(fileType)) {
2777afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_IMAGE;
2778afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isPlayListFileType(fileType)) {
2779afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
2780afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2781afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2782c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
2783afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        values.put(FileColumns.MEDIA_TYPE, mediaType);
2784c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
2785afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (rowId == 0) {
2786afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
27873572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen                String name = values.getAsString(Audio.Playlists.NAME);
2788282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                if (name == null && path == null) {
2789282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                    // MediaScanner will compute the name from the path if we have one
2790afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
2791282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                            "no name was provided when inserting abstract playlist");
2792afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2793afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            } else {
2794a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu                if (path == null) {
2795afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // path might be null for playlists created on the device
2796afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // or transfered via MTP
2797afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
2798afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "no path was provided when inserting new file");
2799afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2800e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2801b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2802f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            // make sure modification date and size are set
2803f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            if (path != null) {
2804f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                File file = new File(path);
2805f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                if (file.exists()) {
2806f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                    values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
280716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.SIZE, file.length());
2808e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
28095d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
2810b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2811afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            Long parent = values.getAsLong(FileColumns.PARENT);
28125d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (parent == null) {
2813e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path != null) {
281410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    long parentId = getParent(helper, db, path);
281516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.PARENT, parentId);
2816e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
28179be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
28189be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            Integer storage = values.getAsInteger(FileColumns.STORAGE_ID);
28199be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (storage == null) {
28209be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int storageId = getStorageId(path);
28219be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                values.put(FileColumns.STORAGE_ID, storageId);
28225d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
2823b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
282410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumInserts++;
2825afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2826afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId);
2827afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
282810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (rowId != -1 && notify) {
2829afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                sendObjectAdded(rowId);
2830afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
28315d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } else {
283210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates++;
283316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.update("files", values, FileColumns._ID + "=?",
2834afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    new String[] { Long.toString(rowId) });
28355d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        }
2836afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2837afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        return rowId;
28381717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    }
28391717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
284010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private Cursor getObjectReferences(DatabaseHelper helper, SQLiteDatabase db, int handle) {
284110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
284210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        Cursor c = db.query("files", mMediaTableColumns, "_id=?",
2843e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
2844e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
2845e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
2846e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
2847afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long playlistId = c.getLong(0);
2848afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
2849afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
2850e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
2851e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return null;
2852e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
285310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumQueries++;
2854e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                return db.rawQuery(OBJECT_REFERENCES_QUERY,
2855afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        new String[] { Long.toString(playlistId) } );
2856e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2857e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
2858e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
2859e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
2860e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2861e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2862e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return null;
2863e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
2864e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
286510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int setObjectReferences(DatabaseHelper helper, SQLiteDatabase db,
286610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            int handle, ContentValues values[]) {
2867e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // first look up the media table and media ID for the object
2868e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        long playlistId = 0;
286910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
287016dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        Cursor c = db.query("files", mMediaTableColumns, "_id=?",
2871e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
2872e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
2873e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
2874e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
2875afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
2876afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
2877e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
2878e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return 0;
2879e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2880afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                playlistId = c.getLong(0);
2881e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2882e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
2883e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
2884e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
2885e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2886e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2887e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (playlistId == 0) {
2888e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            return 0;
2889e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2890e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2891e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // next delete any existing entries
289210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumDeletes++;
289310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        db.delete("audio_playlists_map", "playlist_id=?",
2894e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] { Long.toString(playlistId) });
2895e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2896e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // finally add the new entries
2897e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int count = values.length;
2898e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int added = 0;
2899e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        ContentValues[] valuesList = new ContentValues[count];
2900e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        for (int i = 0; i < count; i++) {
2901e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // convert object ID to audio ID
2902e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long audioId = 0;
2903e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID);
290410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
290516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            c = db.query("files", mMediaTableColumns, "_id=?",
2906e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    new String[] {  Long.toString(objectId) },
2907e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    null, null, null);
2908e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            try {
2909e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null && c.moveToNext()) {
2910afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    int mediaType = c.getInt(1);
291150d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood                    if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) {
2912e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        // we only allow audio files in playlists, so skip
2913e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        continue;
2914e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    }
2915afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    audioId = c.getLong(0);
2916e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2917e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            } finally {
2918e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null) {
2919e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    c.close();
2920e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
2921e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2922e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (audioId != 0) {
2923e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                ContentValues v = new ContentValues();
2924e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId);
2925e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
2926a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added);
2927a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                valuesList[added++] = v;
2928e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
2929e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2930e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (added < count) {
2931e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // we weren't able to find everything on the list, so lets resize the array
2932e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // and pass what we have.
2933e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            ContentValues[] newValues = new ContentValues[added];
2934e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            System.arraycopy(valuesList, 0, newValues, 0, added);
2935e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            valuesList = newValues;
2936e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
2937e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return playlistBulkInsert(db,
2938e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId),
2939e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                valuesList);
2940e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
2941e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2942b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private static final String[] GENRE_LOOKUP_PROJECTION = new String[] {
2943b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres._ID, // 0
2944b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres.NAME, // 1
2945b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    };
2946b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
2947b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private void updateGenre(long rowId, String genre) {
2948b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri uri = null;
2949b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Cursor cursor = null;
2950b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri genresUri = MediaStore.Audio.Genres.getContentUri("external");
2951b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        try {
2952b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // see if the genre already exists
2953b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            cursor = query(genresUri, GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?",
2954b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            new String[] { genre }, null);
2955b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor == null || cursor.getCount() == 0) {
2956b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre does not exist, so create the genre in the genre table
2957b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                ContentValues values = new ContentValues();
2958b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                values.put(MediaStore.Audio.Genres.NAME, genre);
2959b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = insert(genresUri, values);
2960b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            } else {
2961b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre already exists, so compute its Uri
2962b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.moveToNext();
2963b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = ContentUris.withAppendedId(genresUri, cursor.getLong(0));
2964b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
2965b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (uri != null) {
2966b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = Uri.withAppendedPath(uri, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY);
2967b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
2968b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        } finally {
2969b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // release the cursor if it exists
2970b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor != null) {
2971b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.close();
2972b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
2973b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
2974b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
2975b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (uri != null) {
2976b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // add entry to audio_genre_map
2977b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            ContentValues values = new ContentValues();
2978b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
2979b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            insert(uri, values);
2980b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
2981b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    }
2982b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
29835d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood    private Uri insertInternal(Uri uri, int match, ContentValues initialValues) {
2984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
2985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2986d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
2987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
2988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
2989bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
299010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
299110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
299210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
299310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
299410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
299510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStartTime = SystemClock.currentTimeMicro();
299610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
2997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
2998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3000b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
3001b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3002b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3003b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
3004b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3005b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
300710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
300810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null && match != VOLUMES && match != MTP_CONNECTED) {
3009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
301210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
3013819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null
301410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                : helper.getWritableDatabase());
3015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
3017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
301810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues, FileColumns.MEDIA_TYPE_IMAGE, true);
3019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
3021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
3022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3026b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
3027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
302810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3029b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
303010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
3032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
3034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContentUri(uri.getPathSegments().get(0)), rowId);
3035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3039b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
3040b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
304110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3042b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
304310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3044b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
3045b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
3046b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
3047b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            getContentUri(uri.getPathSegments().get(0)), rowId);
3048b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
3049b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3050b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
3051b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
305310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues, FileColumns.MEDIA_TYPE_AUDIO, true);
3054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
3056b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    if (genre != null) {
3057b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        updateGenre(rowId, genre);
3058b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    }
3059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
3064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3065bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
306710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3068ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_genres_map", "genre_id", values);
3069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3070702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3071702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
3076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3077bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
307910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
3081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
3082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3087702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
308910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3090bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                rowId = db.insert("audio_genres", "audio_id", initialValues);
3091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
3093702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
3098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
3099bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
310110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
3103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
3110bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
311210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, values, FileColumns.MEDIA_TYPE_PLAYLIST, true);
3113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
3115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
3121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
3122bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
312410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3125ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_playlists_map", "playlist_id", values);
3126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
313310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues, FileColumns.MEDIA_TYPE_VIDEO, true);
3134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3135b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
3136b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            uri.getPathSegments().get(0)), rowId);
3137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3141c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case AUDIO_ALBUMART: {
314210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                if (helper.mInternal) {
3143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
3144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3145bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = null;
3146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
3147bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
3148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
3149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
3150bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = initialValues;
3151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
315210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3153801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
3154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3158c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
3161bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                return attachVolume(initialValues.getAsString("name"));
3162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3163819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            case MTP_CONNECTED:
316434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                synchronized (mMtpServiceConnection) {
316534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    if (mMtpService == null) {
316634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        Context context = getContext();
316734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        // MTP is connected, so grab a connection to MtpService
316834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        context.bindService(new Intent(context, MtpService.class),
316934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                                mMtpServiceConnection, Context.BIND_AUTO_CREATE);
317034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    }
317134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
3172819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                break;
3173819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
3174afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FILES:
317510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
3176bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                        FileColumns.MEDIA_TYPE_NONE, true);
3177fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                if (rowId > 0) {
3178fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                    newUri = Files.getContentUri(uri.getPathSegments().get(0), rowId);
317910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (initialValues.getAsInteger(MediaStore.Files.FileColumns.FORMAT)
318010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            == MtpConstants.FORMAT_ASSOCIATION) {
318110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        mDirectoryCache.put(
318210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                initialValues.getAsString(MediaStore.MediaColumns.DATA), rowId);
318310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
3184fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                }
3185fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                break;
3186fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood
3187e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
3188afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // don't send a notification if the insert originated from MTP
318910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
3190bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                        FileColumns.MEDIA_TYPE_NONE, false);
3191afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (rowId > 0) {
3192afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    newUri = Files.getMtpObjectsUri(uri.getPathSegments().get(0), rowId);
31935d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                }
31945d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                break;
31955d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
3196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
3198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
3201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3203cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    @Override
3204cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
3205cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                throws OperationApplicationException {
3206cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3207cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // The operations array provides no overall information about the URI(s) being operated
3208cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // on, so begin a transaction for ALL of the databases.
3209cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
3210cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
3211cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase idb = ihelper.getWritableDatabase();
3212cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        idb.beginTransaction();
3213cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase edb = null;
3214cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (ehelper != null) {
3215cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb = ehelper.getWritableDatabase();
3216cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb.beginTransaction();
3217cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3218cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        try {
3219cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentProviderResult[] result = super.applyBatch(operations);
3220cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.setTransactionSuccessful();
3221cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3222cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.setTransactionSuccessful();
3223cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3224cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // Rather than sending targeted change notifications for every Uri
3225cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // affected by the batch operation, just invalidate the entire internal
3226cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // and external name space.
3227cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentResolver res = getContext().getContentResolver();
3228cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
3229cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            return result;
3230cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        } finally {
3231cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.endTransaction();
3232cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3233cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.endTransaction();
3234cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3235cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3236cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    }
3237cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3238cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
32399299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) {
3240b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
3241e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            MediaThumbRequest req = null;
3242e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            try {
3243e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                req = new MediaThumbRequest(
32449299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        getContext().getContentResolver(), path, uri, priority, magic);
3245e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                mMediaThumbQueue.add(req);
3246e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                // Trigger the handler.
3247e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
3248e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                msg.sendToTarget();
3249e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            } catch (Throwable t) {
3250e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Log.w(TAG, t);
3251e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            }
3252b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
3253b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
3254b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
3255b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
3257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
3258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
3259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
3260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
3262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
3263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
3264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
3265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
32669be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            return mExternalStoragePaths[0] + "/" + directoryName + "/" + name + preferredExtension;
3267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private boolean ensureFileExists(String path) {
3271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
3272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
3273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
3274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
3276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
3277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
3278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
3279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
3280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
3281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
3282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
328317ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood            file.getParentFile().mkdirs();
3284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
328517ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood                return file.createNewFile();
3286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
3287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
3288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
3290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
3294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
3295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
3296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
3299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
3300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
3302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
3303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
3304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
33059f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
3306afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3307afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE;
33089f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
33099f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
3310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
3311afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
3313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3315b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
3316b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3317b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
3318b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
3319b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3320b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
3322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO;
3324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
3327afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
3332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
3337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
3340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
3341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
3343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
3348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
3351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
3354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
3358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
3363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
3365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
3368afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3369afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST;
3370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
3378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
3380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
3383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
3385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
3386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
3390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
3391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
3394afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO;
3396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
3399afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3403b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
3404b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3405b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
3406b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
3407b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3408b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
340916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
3410e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
34111717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                where = "_id=" + uri.getPathSegments().get(2);
341216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
3413e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
341416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                out.table = "files";
34151717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                break;
34161717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
3417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
3420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
3423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
3424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
3425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
3426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
3427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
3428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
3431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
3435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
3436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
3438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
3442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
3443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
344410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
344510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
344610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
344710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
344810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
344910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStopTime = SystemClock.currentTimeMicro();
345010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
3452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
3453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3455819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        if (match == VOLUMES_ID) {
3456819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            detachVolume(uri);
3457819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            count = 1;
3458819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else if (match == MTP_CONNECTED) {
345934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (mMtpServiceConnection) {
346034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                if (mMtpService != null) {
346134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // MTP has disconnected, so release our connection to MtpService
346234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    getContext().unbindService(mMtpServiceConnection);
346334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 1;
346434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // mMtpServiceConnection.onServiceDisconnected might not get called,
346534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // so set mMtpService = null here
346634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
346734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } else {
346834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 0;
346934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
347034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
3471819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else {
3472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
3473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
3474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3475819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                        "Unknown URI: " + uri + " match: " + match);
3476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
347710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            database.mNumDeletes++;
3478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
3479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
3481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
3482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
348336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    case MTP_OBJECTS:
3484e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood                    case MTP_OBJECTS_ID:
3485d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        try {
3486d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            // don't send objectRemoved event since this originated from MTP
3487d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = true;
348810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            database.mNumDeletes++;
348910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            count = db.delete("files", sGetTableAndWhereParam.where, whereArgs);
3490d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        } finally {
3491d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = false;
3492d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        }
349336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        break;
349478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    case AUDIO_GENRES_ID_MEMBERS:
349510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
349678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        count = db.delete("audio_genres_map",
349778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
349878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        break;
3499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
350010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
3501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
3502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
3503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
3504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
35053631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // Since there are multiple Uris that can refer to the same files
35063631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // and deletes can affect other objects in storage (like subdirectories
35073631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // or playlists) we will notify a change on the entire volume to make
35083631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // sure no listeners miss the notification.
35093631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                String volume = uri.getPathSegments().get(0);
35103631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volume);
35113631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                getContext().getContentResolver().notifyChange(notifyUri, null);
3512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
3516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
3519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
3520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
3521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3522b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
3523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
352410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
352510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
3526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
352910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumUpdates++;
353010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
353110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
3532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3533b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
3534b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3535b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3536b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
3537b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3538b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
3540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
3541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
35421d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // special case renaming directories via MTP.
35431d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // in this case we must update all paths in the database with
35441d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // the directory name as a prefix
35451d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID)
35461d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    && initialValues != null && initialValues.size() == 1) {
35471d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                String oldPath = null;
3548801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA);
35497f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.remove(newPath);
35501d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                // MtpDatabase will rename the directory first, so we test the new file name
35511d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                if (newPath != null && (new File(newPath)).isDirectory()) {
355210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumQueries++;
35531d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION,
35541d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        userWhere, whereArgs, null, null, null);
35551d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    try {
35561d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null && cursor.moveToNext()) {
35571d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            oldPath = cursor.getString(1);
35581d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
35591d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    } finally {
35601d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null) cursor.close();
35611d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
35621d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    if (oldPath != null) {
35637f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                        mDirectoryCache.remove(oldPath);
35641d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        // first rename the row for the directory
356510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
35661d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        count = db.update(sGetTableAndWhereParam.table, initialValues,
35671d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
35681d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0) {
35691d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            // then update the paths of any files and folders contained in the directory.
35702d114cff87e8cd61123dce29e87d469abae98f6cMike Lockwood                            Object[] bindArgs = new Object[] {oldPath + "/", newPath + "/"};
357110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
35722d114cff87e8cd61123dce29e87d469abae98f6cMike Lockwood                            db.execSQL("UPDATE files SET _data=REPLACE(_data, ?1, ?2);", bindArgs);
35731d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
35741d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
35751d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0 && !db.inTransaction()) {
35761d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            getContext().getContentResolver().notifyChange(uri, null);
35771d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
35781d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        return count;
35791d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
35801d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                }
35811d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            }
35821d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
3583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
3584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
3585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
3586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
3587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
35882658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
35892658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
35902658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        values.remove(MediaStore.Audio.Media.COMPILATION);
359107656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen
3592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
3593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
3594a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
35956006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("artist");
3596a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
3597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
359810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> artistCache = helper.mArtistCache;
3599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
3600a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
3601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
360210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    artistRowId = getKeyIdForName(helper, db,
360310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "artists", "artist_key", "artist",
3604acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
3605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
3606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
3607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
3608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
3609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
3610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
361259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
3613a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
36146006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("album");
3615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
3616801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                            String path = values.getAsString(MediaStore.MediaColumns.DATA);
361759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
36182658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            if (albumartist != null) {
36192658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                albumHash = albumartist.hashCode();
36202658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            } else if (compilation != null && compilation.equals("1")) {
36212658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                // nothing to do, hash already set
362259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
36239289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path == null) {
36249289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    if (match == AUDIO_MEDIA) {
36259289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Log.w(TAG, "Possible multi row album name update without"
36269289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                + " path could give wrong album key");
36279289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    } else {
36289289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        //Log.w(TAG, "Specify path to avoid extra query");
36299289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Cursor c = query(uri,
36309289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                new String[] { MediaStore.Audio.Media.DATA},
36319289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                null, null, null);
36329289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        if (c != null) {
36339289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            try {
36349289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                int numrows = c.getCount();
36359289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                if (numrows == 1) {
36369289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    c.moveToFirst();
36379289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    path = c.getString(0);
36389289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                } else {
36399289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    Log.e(TAG, "" + numrows + " rows for " + uri);
36409289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                }
36419289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            } finally {
36429289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                c.close();
36439289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            }
36449289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        }
36459289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    }
36469289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
36479289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path != null) {
36489289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
36499289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
365059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
36512658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
3652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
3653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
365410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> albumCache = helper.mAlbumCache;
3655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
365659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
365759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
3658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
365910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    albumRowId = getKeyIdForName(helper, db,
366010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "albums", "album_key", "album",
3661a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
3662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
3663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
3664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
3665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
3666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
3667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
3670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
3671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
3672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
3673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
3674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
3675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
3676e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
3677e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
3678e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
3679e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
3680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
368210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
3683afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        count = db.update(sGetTableAndWhereParam.table, values,
3684afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
3685b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        if (genre != null) {
3686b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            if (count == 1 && match == AUDIO_MEDIA_ID) {
3687b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                long rowId = Long.parseLong(uri.getPathSegments().get(3));
3688b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                updateGenre(rowId, genre);
3689b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            } else {
3690b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                // can't handle genres for bulk update or for non-audio files
3691b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                Log.w(TAG, "ignoring genre in update: count = "
3692b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                        + count + " match = " + match);
3693b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            }
3694b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        }
3695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
3696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
3698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
3699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
3700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
3701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
3702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
3703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
3704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
3705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
3706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
3707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
3708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
3709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
3710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
3711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
3712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
3713b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                        computeTakenTime(values);
371410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
3715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
3716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
371701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
371801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
371901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
372001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
372110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumQueries++;
372201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
372301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
372401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
3725b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
3726216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                try {
3727216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    while (c.moveToNext()) {
3728216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        long magic = c.getLong(2);
3729216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        if (magic == 0) {
3730216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                            requestMediaThumbnail(c.getString(1), uri,
3731216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                                    MediaThumbRequest.PRIORITY_NORMAL, 0);
3732216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        }
3733b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
3734216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                } finally {
3735216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    c.close();
3736b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
3737b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
3738b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
3739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
3740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3741f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
3742f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
3743f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    String moveit = uri.getQueryParameter("move");
3744f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    if (moveit != null) {
3745f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
3746f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        if (initialValues.containsKey(key)) {
3747f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int newpos = initialValues.getAsInteger(key);
3748f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            List <String> segments = uri.getPathSegments();
3749f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            long playlist = Long.valueOf(segments.get(3));
3750f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int oldpos = Integer.valueOf(segments.get(5));
375110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            return movePlaylistEntry(helper, db, playlist, oldpos, newpos);
3752f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        }
3753f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        throw new IllegalArgumentException("Need to specify " + key +
3754f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                                " when using 'move' parameter");
3755f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    }
3756f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    // fall through
3757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
375810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumUpdates++;
3759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
3760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
3761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
3762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3764cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // in a transaction, the code that began the transaction should be taking
3765cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // care of notifications once it ends the transaction successfully
3766cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (count > 0 && !db.inTransaction()) {
3767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
3768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
3770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
377210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int movePlaylistEntry(DatabaseHelper helper, SQLiteDatabase db,
377310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            long playlist, int from, int to) {
3774f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        if (from == to) {
3775f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return 0;
3776f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
3777f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        db.beginTransaction();
3778f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        try {
3779f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            int numlines = 0;
378010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates += 3;
3781f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=-1" +
3782f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=" + from +
3783f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " AND playlist_id=" + playlist);
3784f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // We could just run both of the next two statements, but only one of
3785f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // of them will actually do anything, so might as well skip the compile
3786f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // and execute steps.
3787f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            if (from  < to) {
3788f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" +
3789f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " WHERE play_order<=" + to + " AND play_order>" + from +
3790f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
3791f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = to - from + 1;
3792f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            } else {
3793f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" +
3794f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " WHERE play_order>=" + to + " AND play_order<" + from +
3795f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
3796f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = from - to + 1;
3797f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            }
3798f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=" + to +
3799f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=-1 AND playlist_id=" + playlist);
3800f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.setTransactionSuccessful();
3801f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
3802f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    .buildUpon().appendEncodedPath(String.valueOf(playlist)).build();
3803f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            getContext().getContentResolver().notifyChange(uri, null);
3804f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return numlines;
3805f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        } finally {
3806f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.endTransaction();
3807f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
3808f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    }
3809f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
3810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
3811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
3812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
3813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
3815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
3816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
381771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
3818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
381971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
382071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
382171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
382271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
382371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
382471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
382571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
382671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
38275fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            if (db == null) {
38285fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                throw new IllegalStateException("Couldn't open database for " + uri);
38295fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            }
383071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
383171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
383271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
383371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
383471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
383571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
383671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
383771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
383871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
383971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
384071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                String audiopath = c.getString(0);
384171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                int albumid = c.getInt(1);
384271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // Try to get existing album art for this album first, which
384371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // could possibly have been obtained from a different file.
384471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // If that fails, try to get it from this specific file.
384571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
384671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
38473fa7593ce394cdaad4a3db622d415fd8497f4a9dMarco Nelissen                    pfd = openFileHelper(newUri, mode);
384871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
384971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    // That didn't work, now try to get it from the specific file
385071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, null);
385171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
385271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
385371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            c.close();
385471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
385571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
385671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
3857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
3858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd = openFileHelper(uri, mode);
3859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
386071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
386171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
386271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
386371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
386471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
3865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
3866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
3867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
3868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
3869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
3870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
38725fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                if (db == null) {
38735fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                    throw new IllegalStateException("Couldn't open database for " + uri);
38745fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                }
3875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
3876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
387771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
3878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
3879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
3880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
3881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
3882a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen                        null, null, null, null, MediaStore.Audio.Media.TRACK);
388371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                if (c.moveToFirst()) {
3884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String audiopath = c.getString(0);
388571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, uri);
3886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                c.close();
3888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
388971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
389071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
389171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
3892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
3894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
389710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper;
3898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
3899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
3900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
3901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
3902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
390410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void makeThumbAsync(DatabaseHelper helper, SQLiteDatabase db,
390510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String path, long album_id) {
39068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
39078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
39088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
39098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
39108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
39118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
39128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
39138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
39148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
39158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
3916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
391710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        d.helper = helper;
3918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
3919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
3920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
3921a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
39228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
39238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
39248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
39258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
39268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
39278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
39288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
39298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
39308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
39318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
39328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
3933b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
3934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
3935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
39378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
39388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
39398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
39408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
3941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
3943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
3944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
3945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
3946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
39478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
39488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
3949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
3950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3951d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
39523f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // same directory as the media file, except if that directory is
39533f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // is the root directory of the sd card or the download directory.
3954d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
3955d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
3956d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
3957d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
3958d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
3959d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
39608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
3961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
3962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
3963d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
39643f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String artPath = path.substring(0, lastSlash);
39659be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    String sdroot = mExternalStoragePaths[0];
39660fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen                    String dwndir = Environment.getExternalStoragePublicDirectory(
39672f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
3968d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
3969d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
3970d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
3971d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
3972d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
3973ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                        } else if (!artPath.equalsIgnoreCase(sdroot) &&
3974ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                                !artPath.equalsIgnoreCase(dwndir)) {
3975d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
3976d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
3977d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
3978d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
3979d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
3980d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
3981d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
3982d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
3983d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
3984d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
3985d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3986d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
3987d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
3988d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
3989d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
3990d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3991d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
3992d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
3993d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
3994d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
3995d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3996d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
3997d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
3998d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
3999d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
4000d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
4001d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4002d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
4003d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4004d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4005d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
4006d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
4007d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
4008d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
4009d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4010d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
40113f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        File file = new File(artPath, bestmatch);
4012d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
4013d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            compressed = new byte[(int)file.length()];
4014d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
4015d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
4016d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
4017d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
4018d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
4019d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
4020d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
4021d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
4022d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
4023d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
40298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
40308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
4031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
40328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
40338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
4034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
40358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
403610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    Uri getAlbumArtOutputUri(DatabaseHelper helper, SQLiteDatabase db, long album_id, Uri albumart_uri) {
40378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
40388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
40398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
40408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
4041801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            Cursor c = query(albumart_uri, new String [] { MediaStore.MediaColumns.DATA },
40428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
4043d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            try {
4044d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null && c.moveToFirst()) {
4045d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    String albumart_path = c.getString(0);
4046d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    if (ensureFileExists(albumart_path)) {
4047d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                        out = albumart_uri;
4048d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    }
4049d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                } else {
4050d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    albumart_uri = null;
4051d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                }
4052d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            } finally {
4053d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null) {
4054d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    c.close();
4055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
405771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
405871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
40598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
40608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
40618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
40628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
406310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
4064801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
40658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
40668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
4067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
40688a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
40698a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
40708a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
40718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
40728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
40738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
40748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
40758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
40768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
40778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
40788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) {
40798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean success = false;
40808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
40818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            OutputStream outstream = getContext().getContentResolver().openOutputStream(out);
40828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
40838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
40848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
40858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
40868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
40878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = true;
40888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
408970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann                success = bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream);
4090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
40918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
40928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            outstream.close();
40938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (FileNotFoundException ex) {
40948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
4095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (IOException ex) {
40968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
40978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
40988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (!success) {
40998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // the thumbnail was not written successfully, delete the entry that refers to it
41008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            getContext().getContentResolver().delete(out, null, null);
4101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
41028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
41038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
410471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor getThumb(SQLiteDatabase db, String path, long album_id,
410571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Uri albumart_uri) {
410671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
410771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
410871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
410971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
411071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
411171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
411271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
411371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
411471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
41158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
4116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
41178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
411871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
41198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
41208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
41228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
41238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
41258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
41268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
41278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
41288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
41298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
41308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
413270676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final Resources r = getContext().getResources();
413370676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size);
413470676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) {
41358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
41368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
41378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
41388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
41398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
41418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
41428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
41438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
41448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
41458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
41468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
41478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
41488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
41498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
4151a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false);
4152a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    if (nbm != null && nbm != bm) {
4153a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm.recycle();
4154a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm = nbm;
4155a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    }
41568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
41578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
41588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
41598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
41608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
41618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
416271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
41638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
41648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
416571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
416671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
416771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
416871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
416924001394f571b1f0378840cbf299288e4df10508Bjorn Bringert                return ParcelFileDescriptor.fromData(compressed, "albumthumb");
417071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
417171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
417271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
4173a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This one needs to actually be saved on the sd card.
4174a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This is wrapped in a transaction because there are various things
4175a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // that could go wrong while generating the thumbnail, and we only want
4176a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // to update the database when all steps succeeded.
4177a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            d.db.beginTransaction();
4178a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            try {
417910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Uri out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri);
4180a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen
4181a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (out != null) {
4182a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    writeAlbumArt(need_to_recompress, out, compressed, bm);
4183a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    getContext().getContentResolver().notifyChange(MEDIA_URI, null);
4184a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    ParcelFileDescriptor pfd = openFileHelper(out, "r");
4185a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    d.db.setTransactionSuccessful();
4186a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    return pfd;
4187a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                }
4188a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (FileNotFoundException ex) {
4189a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
4190a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (UnsupportedOperationException ex) {
4191a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
4192a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } finally {
4193a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                d.db.endTransaction();
4194a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (bm != null) {
4195a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    bm.recycle();
419671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
419771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
41988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
419971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
4200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
4204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
4205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
4206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
4207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
4208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
4209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
421059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
421159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
421259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
4213a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
4214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
4215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
4216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
4217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
4218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
421910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getKeyIdForName(DatabaseHelper helper, SQLiteDatabase db,
422010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String table, String keyField, String nameField,
422159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
4222a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
4223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
4224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
422651cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            rawName = MediaStore.UNKNOWN_STRING;
4227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
4229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
423151cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            // shouldn't happen, since we only get null keys for null inputs
423251cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            Log.e(TAG, "null key", new Exception());
4233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
4234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
423659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
4237e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen        boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName);
423859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
42392658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // To distinguish same-named albums, we append a hash. The hash is based
42402658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // on the "album artist" tag if present, otherwise on the "compilation" tag
42412658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // if present, otherwise on the path.
424259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
424359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
424459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
424559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
424659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
4247a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
424859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
4249a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
4250a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
4251a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
425259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
425359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
4254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
425510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
4256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
4257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
4260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
4261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
4262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
4263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
4264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
426510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumInserts++;
4266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
426759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
4268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
426910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            makeThumbAsync(helper, db, path, rowId);
4270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
4272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
4273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
4274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
4275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
4279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
4280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
4281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
4282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
4284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
4285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
4286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
4287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
4288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
4289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
4290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
429110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
4292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
4293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
4294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
4295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
4296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
4300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
4301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
4302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
4303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
4306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (c != null) c.close();
4307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
430959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
431059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
4311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
4313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
4317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
4318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
4319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
4320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
4321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
4323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
4324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
4325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
4326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
4328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
4329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
4330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
4332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
4333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
4335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
4336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
4337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
4338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
4339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
4340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
4341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
4342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
4343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
4347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
4348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
4349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
4350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
4351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
4352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
4355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
4356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
4361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
4363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
4364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
4366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
4367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (uri.getPathSegments().size() > 1) {
4368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
4369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
4372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4374fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isMediaDatabaseName(String name) {
4375fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
4376fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4377fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4378fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (EXTERNAL_DATABASE_NAME.equals(name)) {
4379fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4380fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4381fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (name.startsWith("external-")) {
4382fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4383fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4384fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
4385fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
4386fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
4387fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isInternalMediaDatabaseName(String name) {
4388fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
4389fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
4390fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
4391fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
4392fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
4393fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
4394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
4396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
4397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
4398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
4400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
4401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
440375392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
4404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
4405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
4406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
4409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
4410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
4411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4413993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            Context context = getContext();
441410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper helper;
4415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
441610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true,
4417fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                        false, mObjectRemovedCallback);
4418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
4419993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                if (Environment.isExternalStorageRemovable()) {
44209be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    String path = mExternalStoragePaths[0];
4421993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    int volumeID = FileUtils.getFatVolumeId(path);
4422993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
4423993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
4424993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // generate database name based on volume ID
4425993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
442610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbName, false,
4427fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
4428993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    mVolumeId = volumeID;
4429993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                } else {
4430993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // external database name should be EXTERNAL_DATABASE_NAME
4431993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // however earlier releases used the external-XXXXXXXX.db naming
4432993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // for devices without removable storage, and in that case we need to convert
4433993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // to this new convention
4434993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME);
4435993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (!dbFile.exists()) {
4436993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // find the most recent external database and rename it to
4437993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // EXTERNAL_DATABASE_NAME, and delete any other older
4438993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // external database files
4439993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        File recentDbFile = null;
4440993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        for (String database : context.databaseList()) {
4441993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (database.startsWith("external-")) {
4442993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                File file = context.getDatabasePath(database);
4443993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                if (recentDbFile == null) {
4444993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
4445993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else if (file.lastModified() > recentDbFile.lastModified()) {
4446993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile.delete();
4447993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
4448993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else {
4449993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    file.delete();
4450993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                }
4451993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
4452993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
4453993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        if (recentDbFile != null) {
4454993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (recentDbFile.renameTo(dbFile)) {
4455993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.d(TAG, "renamed database " + recentDbFile.getName() +
4456993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
4457993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            } else {
4458993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.e(TAG, "Failed to rename database " + recentDbFile.getName() +
4459993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
4460993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // This shouldn't happen, but if it does, continue using
4461993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // the file under its old name
4462993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                dbFile = recentDbFile;
4463993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
4464993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
4465993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME
4466993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    }
446710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbFile.getName(), false,
4468fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
4469993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                }
4470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
4471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
4472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
447410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            mDatabases.put(volume, helper);
4475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
447610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (!helper.mInternal) {
4477ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                // create default directories (only happens on first boot)
447810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                createDefaultFolders(helper, helper.getWritableDatabase());
4479ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
4480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
44819be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                File[] files = new File(mExternalStoragePaths[0], ALBUM_THUMB_FOLDER).listFiles();
4482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
4483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
4484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
4485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
4488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
4489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
4490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
4491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
4492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
4494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (cursor != null) cursor.close();
4495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
4498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
4499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
4500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
4501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
4502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
4507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
4508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
4511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
4512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
4513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
4514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
4515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
4516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
4517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
451875392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
4519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
4520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
4521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
4524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
4525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
4526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
4527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
4528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
4529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
4530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
4533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
4534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
4535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
4537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
4538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
4539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
4540e9ee0248d62f3badef8a554f35f78e9116ef8a5cMike Lockwood            } catch (Exception e) {
4541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
4542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
4545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
4546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
4549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
4550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
4553ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer    private static final boolean LOCAL_LOGV = false;
4554971a2ef5165e2072c76bf25049fdda94187019c2Dianne Hackborn
4555bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    static final int DATABASE_VERSION = 408;
4556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
4557993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private static final String EXTERNAL_DATABASE_NAME = "external.db";
4558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
4560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
4561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
4563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
4564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
4565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
4567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
4569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
4571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
4572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
45730027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    // current FAT volume ID
4574993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private int mVolumeId = -1;
45750027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
4576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
4577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
4578268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen    static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs";
4579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
4581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
4582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
45831717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS
458416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    // are stored in the "files" table, so do not renumber them unless you also add
45851717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // a corresponding database upgrade step for it.
4586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
4587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
4588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
4589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
4590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
4592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
4593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
4594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
4595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
4596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
4597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
4598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
4599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
4600bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen    private static final int AUDIO_GENRES_ALL_MEMBERS = 109;
4601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
4602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
4603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
4604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
4605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
4606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
4607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
4608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
4609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
4610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
4611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
461271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
4613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
4615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
4616b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
4617b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
4618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
4620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
4621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4622a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
4623a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
4624a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
4625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
4627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
46280027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private static final int FS_ID = 600;
4629704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen    private static final int VERSION = 601;
46300027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
463116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES = 700;
463216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES_ID = 701;
4633a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
4634e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    // Used only by the MTP implementation
4635e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS = 702;
4636e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS_ID = 703;
4637e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECT_REFERENCES = 704;
4638819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // UsbReceiver calls insert() and delete() with this URI to tell us
4639819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // when MTP is connected and disconnected
4640819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    private static final int MTP_CONNECTED = 705;
4641b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
4642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
4643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
4644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4645b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
4646b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
4647b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
4648b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
46491d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    private static final String[] PATH_PROJECTION = new String[] {
46501d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood        MediaStore.MediaColumns._ID,
46511d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            MediaStore.MediaColumns.DATA,
46521d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    };
46531d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
4654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
4655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
4656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
4657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
4658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4659b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
4660b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
4661b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
4662b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
4663b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
4664b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
4665e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private static final String OBJECT_REFERENCES_QUERY =
4666afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map"
4667afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?"
4668afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER;
4669e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
4670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
4671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
4672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
4673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
4674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
4675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
4676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
4678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
4679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
4680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
4681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
4682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
4683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
4684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
4685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
4686bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen        URI_MATCHER.addURI("media", "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS);
4687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
4688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
4689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
4690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
4691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
4692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
4693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
4694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
4695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
4696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
4697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
469871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
4699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
4701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
4702b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
4703b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
4704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
4706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
47070027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        URI_MATCHER.addURI("media", "*/fs_id", FS_ID);
4708704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        URI_MATCHER.addURI("media", "*/version", VERSION);
47090027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
4710819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED);
4711819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
4712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
4713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
4714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4715b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        // Used by MTP implementation
471616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file", FILES);
471716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file/#", FILES_ID);
4718e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS);
4719e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID);
4720e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES);
4721b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
4722a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
4723a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
4724a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
4725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
4726a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
4727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
4728a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
4729a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
4730a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
4731a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
4732a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
4733a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
4734a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
4735a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
4736a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
4737a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
4738a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
4739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
474010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
474110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    @Override
474210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
474310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        Collection<DatabaseHelper> foo = mDatabases.values();
474410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        for (DatabaseHelper dbh: foo) {
474510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            StringBuilder s = new StringBuilder();
474610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            s.append(dbh.mName);
474710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            s.append(": ");
474810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            SQLiteDatabase db = dbh.getReadableDatabase();
474910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (db == null) {
475010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append("null");
475110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
475210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append("version " + db.getVersion() + ", ");
475310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Cursor c = db.query("files", new String[] {"count(*)"}, null, null, null, null, null);
475410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                try {
475510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (c != null && c.moveToFirst()) {
475610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        int num = c.getInt(0);
475710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        s.append(num + " rows, ");
475810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    } else {
475910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        s.append("couldn't get row count, ");
476010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
476110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                } finally {
476210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (c != null) {
476310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        c.close();
476410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
476510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                }
476610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append(dbh.mNumInserts + " inserts, ");
476710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append(dbh.mNumUpdates + " updates, ");
476810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append(dbh.mNumDeletes + " deletes, ");
476910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                s.append(dbh.mNumQueries + " queries, ");
477010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                if (dbh.mScanStartTime != 0) {
477110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    s.append("scan started " + DateUtils.formatDateTime(getContext(),
477210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            dbh.mScanStartTime / 1000,
477310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            DateUtils.FORMAT_SHOW_DATE
477410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            | DateUtils.FORMAT_SHOW_TIME
477510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            | DateUtils.FORMAT_ABBREV_ALL));
477610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (dbh.mScanStopTime < dbh.mScanStartTime) {
477710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        s.append(" (ongoing)");
477810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    } else {
477910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        s.append(" (" + DateUtils.formatElapsedTime(
478010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                (dbh.mScanStopTime - dbh.mScanStartTime) / 1000000) + ")");
478110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
478210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                }
478310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
478410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            writer.println(s);
478510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        }
478610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (mMediaScannerVolume != null) {
478710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            writer.println("Scanning: " + mMediaScannerVolume);
478810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        }
478910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    }
479010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
4791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
4792