MediaProvider.java revision 3fa4c1f0f2d9631aa41567b6c2efb2716421ba40
1702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/*
2702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
3702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
4702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * you may not use this file except in compliance with the License.
6702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * You may obtain a copy of the License at
7702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
8702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
10702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * See the License for the specific language governing permissions and
14702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * limitations under the License.
15702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
16702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
17702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpackage com.android.providers.media;
18702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
19007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.ACCESS_CACHE_FILESYSTEM;
20007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.READ_EXTERNAL_STORAGE;
21007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
22f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
23007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkeyimport static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
24007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
25702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.app.SearchManager;
26bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.BroadcastReceiver;
27bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ComponentName;
28bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProvider;
29bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderOperation;
30bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentProviderResult;
31bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentResolver;
32bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentUris;
33bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ContentValues;
34bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Context;
35bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.Intent;
36bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.IntentFilter;
37bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.OperationApplicationException;
38bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.ServiceConnection;
39ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.content.SharedPreferences;
40bdd3b8337b01920822c128b1ad1be202e22d070cOwen Linimport android.content.UriMatcher;
413e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissenimport android.content.pm.PackageManager;
4290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissenimport android.content.pm.PackageManager.NameNotFoundException;
4370676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.content.res.Resources;
44702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
45ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils;
460027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor;
47702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
48702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap;
51702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory;
52b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwoodimport android.media.MediaFile;
53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner;
5438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection;
5538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.media.MediaScannerConnection.MediaScannerConnectionClient;
56b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
5790345783ad297da6059398cab174687de6f36a5bMike Lockwoodimport android.mtp.MtpConstants;
589be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwoodimport android.mtp.MtpStorage;
59702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
60702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
6138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport android.os.Bundle;
62702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
63702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler;
64ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread;
65702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message;
66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor;
67702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process;
68d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwoodimport android.os.RemoteException;
6910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.os.SystemClock;
70c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwoodimport android.os.storage.StorageManager;
711f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwoodimport android.os.storage.StorageVolume;
72ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwoodimport android.preference.PreferenceManager;
73702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns;
74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore;
75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio;
764eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissenimport android.provider.MediaStore.Audio.Playlists;
77a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Linimport android.provider.MediaStore.Files;
7870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Files.FileColumns;
79702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images;
8070676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmannimport android.provider.MediaStore.Images.ImageColumns;
81702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns;
82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video;
83702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
8410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport android.text.format.DateUtils;
85702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
86702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
87ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.ErrnoException;
88ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.IoUtils;
89ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.Libcore;
90ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.OsConstants;
91ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkeyimport libcore.io.StructStat;
92ca709d4f9f6ef66337b96010a150e30a5888855eJeff Sharkey
93702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
9410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.FileDescriptor;
95702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
96702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
97702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
98702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
9910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.io.PrintWriter;
100cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList;
10110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissenimport java.util.Collection;
102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
105f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List;
10638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissenimport java.util.Locale;
107b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
1088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/**
111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details.
112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the
113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index).  The content visible at content://media/external/...
114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card.
115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider {
117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri MEDIA_URI = Uri.parse("content://media");
118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");
119b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int ALBUM_THUMB = 1;
120b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int IMAGE_THUMB = 2;
121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>();
123d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen    private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>();
124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
125007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /** Resolved canonical path to external storage. */
126007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private static final String sExternalPath;
127007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /** Resolved canonical path to cache storage. */
128007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private static final String sCachePath;
1295943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen    /** Resolved canonical path to legacy storage. */
1305943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen    private static final String sLegacyPath;
131007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
132007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    static {
133007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
134007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            sExternalPath = Environment.getExternalStorageDirectory().getCanonicalPath();
135007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            sCachePath = Environment.getDownloadCacheDirectory().getCanonicalPath();
1365943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen            sLegacyPath = Environment.getLegacyExternalStorageDirectory().getCanonicalPath();
137007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } catch (IOException e) {
138007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throw new RuntimeException("Unable to resolve canonical paths", e);
139007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
140007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
141007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
1425d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey    private StorageManager mStorageManager;
1435d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey
1447f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    // In memory cache of path<->id mappings, to speed up inserts during media scan
1457f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    HashMap<String, Long> mDirectoryCache = new HashMap<String, Long>();
1467f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
1478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
1488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
1498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
1508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
1518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
1528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
15320434e032e498b716f87cce2f23dd646819218bfRay Chen    // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest.
15420434e032e498b716f87cce2f23dd646819218bfRay Chen    private MediaThumbRequest mCurrentThumbRequest = null;
155b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
156b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
157b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
158b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
159f7cc647081f0421c0247de275fa0754b29938c07Mike Lockwood    private boolean mCaseInsensitivePaths;
1609be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private static String[] mExternalStoragePaths;
16117ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood
162a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
164a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
166a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
167a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
168a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
169a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
170a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
171a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
172a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
173a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
174a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
175a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
176a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
177a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
178a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
179a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
180ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
181ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
182a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
183a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
184a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
185a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
186a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
187a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
188a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
189a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
190a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
191a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
19263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
193a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
194a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
195a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
196a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
197a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
198a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
199a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
200a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
201a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
20263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
20363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
204e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen            " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" +
20563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
206a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
207a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
20863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
20963f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
210a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
211a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTableColumns = new String[] {
21216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            FileColumns._ID,
213afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            FileColumns.MEDIA_TYPE,
2141717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    };
2151717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
2167f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    private static final String[] sIdOnlyColumn = new String[] {
2177f36494e085c26c69cd5925e54028822025eff29Marco Nelissen        FileColumns._ID
2187f36494e085c26c69cd5925e54028822025eff29Marco Nelissen    };
2197f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
220166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    private static final String[] sDataOnlyColumn = new String[] {
221166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        FileColumns.DATA
222166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen    };
223166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
224a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen    private static final String[] sMediaTypeDataId = new String[] {
225a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.MEDIA_TYPE,
226a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns.DATA,
227a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        FileColumns._ID
2284eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    };
2294eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen
2304eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    private static final String[] sPlaylistIdPlayOrder = new String[] {
2314eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen        Playlists.Members.PLAYLIST_ID,
2324eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen        Playlists.Members.PLAY_ORDER
2334eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen    };
234a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
235a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
236a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
23701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    private static final String CANONICAL = "canonical";
23801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
2431f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                StorageVolume storage = (StorageVolume)intent.getParcelableExtra(
2441f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        StorageVolume.EXTRA_STORAGE_VOLUME);
2451f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // If primary external storage is ejected, then remove the external volume
2461f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                // notify all cursors backed by data on that volume.
2471f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                if (storage.getPath().equals(mExternalStoragePaths[0])) {
2481f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    detachVolume(Uri.parse("content://media/external"));
2491f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    sFolderArtMap.clear();
2501f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    MiniThumbFile.reset();
2511f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                } else {
2521f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // If secondary external storage is ejected, then we delete all database
2531f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    // entries for that storage from the files table.
2541f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    synchronized (mDatabases) {
2551f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        DatabaseHelper database = mDatabases.get(EXTERNAL_VOLUME);
2561f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        Uri uri = Uri.parse("file://" + storage.getPath());
2571f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        if (database != null) {
2581f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            try {
2591f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // Send media scanner started and stopped broadcasts for apps that rely
2601f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // on these Intents for coarse grained media database notifications.
2611f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2621f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
2631f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood
2641f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                // don't send objectRemoved events - MTP be sending StorageRemoved anyway
2651f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = true;
2661f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.d(TAG, "deleting all entries for storage " + storage);
2671f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                SQLiteDatabase db = database.getWritableDatabase();
2684b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // First clear the file path to disable the _DELETE_FILE database hook.
2694b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // We do this to avoid deleting files if the volume is remounted while
2704b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // we are still processing the unmount event.
2714b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                ContentValues values = new ContentValues();
27276611f01202d47396de5f5e9a3341ecc572900c4Marco Nelissen                                values.putNull(Files.FileColumns.DATA);
2734b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String where = FileColumns.STORAGE_ID + "=?";
2744b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                String[] whereArgs = new String[] { Integer.toString(storage.getStorageId()) };
2759b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                database.mNumUpdates++;
2764b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                db.update("files", values, where, whereArgs);
2774b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // now delete the records
2789b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                database.mNumDeletes++;
2799b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                int numpurged = db.delete("files", where, whereArgs);
2809b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                logToDb(db, "removed " + numpurged +
2819b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                        " rows for ejected filesystem " + storage.getPath());
2824b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                // notify on media Uris as well as the files Uri
2834b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2844b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Audio.Media.getContentUri(EXTERNAL_VOLUME), null);
2854b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2864b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Images.Media.getContentUri(EXTERNAL_VOLUME), null);
2874b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                context.getContentResolver().notifyChange(
2884b230ed7c44706e56574d8d7481169b0ad2d0108Mike Lockwood                                        Video.Media.getContentUri(EXTERNAL_VOLUME), null);
2891f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.getContentResolver().notifyChange(
2901f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        Files.getContentUri(EXTERNAL_VOLUME), null);
2911f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } catch (Exception e) {
2921f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                Log.e(TAG, "exception deleting storage entries", e);
2931f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            } finally {
2941f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                context.sendBroadcast(
2951f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                        new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
2961f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                                mDisableMtpObjectCallbacks = false;
2971f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                            }
2981f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                        }
2991f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                    }
3001f3014aa084584f4b0e788e3a67c19057b9ea292Mike Lockwood                }
301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
305d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    // set to disable sending events when the operation originates from MTP
306d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private boolean mDisableMtpObjectCallbacks;
307d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
308d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final SQLiteDatabase.CustomFunction mObjectRemovedCallback =
309d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                new SQLiteDatabase.CustomFunction() {
310d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void callback(String[] args) {
3117f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // We could remove only the deleted entry from the cache, but that
3127f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // requires the path, which we don't have here, so instead we just
3137f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // clear the entire cache.
3147f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // TODO: include the path in the callback and only remove the affected
3157f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            // entry from the cache
3167f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            mDirectoryCache.clear();
317d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            // do nothing if the operation originated from MTP
318d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mDisableMtpObjectCallbacks) return;
319d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
320d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            Log.d(TAG, "object removed " + args[0]);
321d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            IMtpService mtpService = mMtpService;
322d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            if (mtpService != null) {
323d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                try {
324d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    sendObjectRemoved(Integer.parseInt(args[0]));
325d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                } catch (NumberFormatException e) {
326d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                    Log.e(TAG, "NumberFormatException in mObjectRemovedCallback", e);
327d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                }
328d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
329d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
330d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
331d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
337fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static final class DatabaseHelper extends SQLiteOpenHelper {
338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
3395524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        final String mName;
340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
341fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final boolean mEarlyUpgrade;
342fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        final SQLiteDatabase.CustomFunction mObjectRemovedCallback;
3435524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        boolean mUpgradeAttempted; // Used for upgrade error handling
34410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumQueries;
34510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumUpdates;
34610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumInserts;
34710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        int mNumDeletes;
34810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStartTime;
34910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        long mScanStopTime;
350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
355fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        public DatabaseHelper(Context context, String name, boolean internal,
356fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                boolean earlyUpgrade,
357fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                SQLiteDatabase.CustomFunction objectRemovedCallback) {
35890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            super(context, name, null, getDatabaseVersion(context));
359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
3605524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mName = name;
361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
362fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mEarlyUpgrade = earlyUpgrade;
363fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            mObjectRemovedCallback = objectRemovedCallback;
3640e2a2386b39972286df21f4db5a9dd1df548c34dJeff Brown            setWriteAheadLoggingEnabled(true);
365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
37290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, 0, getDatabaseVersion(mContext));
373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
3815524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = true;
38290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            updateDatabase(mContext, db, mInternal, oldV, newV);
383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
385db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        @Override
386db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        public synchronized SQLiteDatabase getWritableDatabase() {
3875524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            SQLiteDatabase result = null;
3885524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            mUpgradeAttempted = false;
3895524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            try {
3905524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
3915524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            } catch (Exception e) {
3925524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                if (!mUpgradeAttempted) {
3935524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    Log.e(TAG, "failed to open database " + mName, e);
3945524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                    return null;
3955524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                }
3965524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
3975524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
3985524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // If we failed to open the database during an upgrade, delete the file and try again.
3995524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // This will result in the creation of a fresh database, which will be repopulated
4005524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            // when the media scanner runs.
4015524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            if (result == null && mUpgradeAttempted) {
402450d884f1bd5de323a645ce1acfae40fb91b8cb0jangwon.lee                mContext.deleteDatabase(mName);
4035524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood                result = super.getWritableDatabase();
4045524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            }
4055524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood            return result;
4065524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood        }
4075524b8957701b458a24605ff4f1d953e7b847c8fMike Lockwood
408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
409993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * For devices that have removable storage, we support keeping multiple databases
410993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * to allow users to switch between a number of cards.
411993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood         * On such devices, touch this particular database and garbage collect old databases.
412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
41736d7136bebac6ea5738fb653a74dcd6c71e4cd58Dmitry Dolinsky
418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
420fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mEarlyUpgrade) return; // Doing early upgrade.
421fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
422fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            if (mObjectRemovedCallback != null) {
423fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                db.addCustomFunction("_OBJECT_REMOVED", 1, mObjectRemovedCallback);
424fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            }
425d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
426993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            // the code below is only needed on devices with removable storage
427993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            if (!Environment.isExternalStorageRemovable()) return;
428993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
48934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood    // synchronize on mMtpServiceConnection when accessing mMtpService
490d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private IMtpService mMtpService;
491d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
492d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private final ServiceConnection mMtpServiceConnection = new ServiceConnection() {
493d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood         public void onServiceConnected(ComponentName className, android.os.IBinder service) {
49434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
49534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = IMtpService.Stub.asInterface(service);
49634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
497d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
498d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
499d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        public void onServiceDisconnected(ComponentName className) {
50034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (this) {
50134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                mMtpService = null;
50234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
503d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
504d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    };
505d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
506ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    private static final String[] sDefaultFolderNames = {
507ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MUSIC,
508ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PODCASTS,
509ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_RINGTONES,
510ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_ALARMS,
511ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_NOTIFICATIONS,
512ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_PICTURES,
513ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_MOVIES,
514ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DOWNLOADS,
515ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        Environment.DIRECTORY_DCIM,
516ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    };
517ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
518ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    // creates default folders (Music, Downloads, etc)
51910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void createDefaultFolders(DatabaseHelper helper, SQLiteDatabase db) {
520ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // Use a SharedPreference to ensure we only do this once.
521ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // We don't want to annoy the user by recreating the directories
522ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        // after she has deleted them.
523ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
524ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        if (prefs.getInt("created_default_folders", 0) == 0) {
525ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            for (String folderName : sDefaultFolderNames) {
526ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                File file = Environment.getExternalStoragePublicDirectory(folderName);
527ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                if (!file.exists()) {
528ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                    file.mkdirs();
52910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    insertDirectory(helper, db, file.getAbsolutePath());
530ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                }
531ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            }
532ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
533ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            SharedPreferences.Editor e = prefs.edit();
534ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.clear();
535ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.putInt("created_default_folders", 1);
536ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood            e.commit();
537ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        }
538ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
539ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
5409311c7ff9d35ca3acc908da3da7a79fbf7a8da6bMarco Nelissen    public static int getDatabaseVersion(Context context) {
54190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        try {
54290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            return context.getPackageManager().getPackageInfo(
54390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    context.getPackageName(), 0).versionCode;
54490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        } catch (NameNotFoundException e) {
54590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            throw new RuntimeException("couldn't get version code for " + context);
54690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
54790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    }
54890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen
549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
551d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        final Context context = getContext();
552d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
5535d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey        mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
5545d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey
555acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
556acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums._ID);
557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
559acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
561acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
566acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
567acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
568acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
569acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                MediaStore.Audio.Albums.ALBUM_ART);
570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
57163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
57263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
573d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        "%1", context.getString(R.string.artist_label));
574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
579d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        context.registerReceiver(mUnmountReceiver, iFilter);
580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
581c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        StorageManager storageManager =
582c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood                (StorageManager)context.getSystemService(Context.STORAGE_SERVICE);
583c47e4f2921312098eddc5fe49b080e0f2df60e81Mike Lockwood        mExternalStoragePaths = storageManager.getVolumePaths();
5849be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
592ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND);
593ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        ht.start();
594ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        mThumbHandler = new Handler(ht.getLooper()) {
595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
597b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
598b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
59920434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest = mMediaThumbQueue.poll();
600b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
60120434e032e498b716f87cce2f23dd646819218bfRay Chen                    if (mCurrentThumbRequest == null) {
602b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
603b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
604b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
60520434e032e498b716f87cce2f23dd646819218bfRay Chen                            File origFile = new File(mCurrentThumbRequest.mPath);
6064d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            if (origFile.exists() && origFile.length() > 0) {
60720434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.execute();
6084d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            } else {
6094d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                // original file hasn't been stored yet
6104d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                synchronized (mMediaThumbQueue) {
61120434e032e498b716f87cce2f23dd646819218bfRay Chen                                    Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath);
6124d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
6134d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
614b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
6151d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
6161d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                        } catch (UnsupportedOperationException ex) {
6171d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // This could happen if we unplug the sd card during insert/update/delete
6181d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // See getDatabaseForUri.
6191d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
62022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                        } catch (OutOfMemoryError err) {
62122c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            /*
62222c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Note: Catching Errors is in most cases considered
62322c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * bad practice. However, in this case it is
62422c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * motivated by the fact that corrupt or very large
62522c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * images may cause a huge allocation to be
62622c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * requested and denied. The bitmap handling API in
62722c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * Android offers no other way to guard against
62822c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             * these problems than by catching OutOfMemoryError.
62922c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                             */
63022c5095b70a9fd6304fcf2366e8e40e37ae95764Erik Rydgren                            Log.w(TAG, err);
631b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
63220434e032e498b716f87cce2f23dd646819218bfRay Chen                            synchronized (mCurrentThumbRequest) {
63320434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE;
63420434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.notifyAll();
635b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
636b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
637b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
638b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
639b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
640b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
641b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
642b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
644d63eb65db514ad7064951f221f0278a3f2293411Jeff Sharkey                    IoUtils.closeQuietly(makeThumbInternal(d));
645b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
646b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
647b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
6488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
655395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_FILES = "files";
656395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_ALBUM_ART = "album_art";
657395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_THUMBNAILS = "thumbnails";
658395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey    private static final String TABLE_VIDEO_THUMBNAILS = "videothumbnails";
659395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
660afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String IMAGE_COLUMNS =
661afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_size,_display_name,mime_type,title,date_added," +
662afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
663bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name," +
664bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "width,height";
665bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
666bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String IMAGE_COLUMNSv407 =
667bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_size,_display_name,mime_type,title,date_added," +
668bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "date_modified,description,picasa_id,isprivate,latitude,longitude," +
669afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken,orientation,mini_thumb_magic,bucket_id,bucket_display_name";
670afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
671805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv99 =
672afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added," +
673afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
674afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
675afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark";
676afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
677805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen    private static final String AUDIO_COLUMNSv100 =
678805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "_data,_display_name,_size,mime_type,date_added," +
679805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
680805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
681805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen                        "bookmark,album_artist";
682805b0b734dd5a73e2f77cd8d818a07a8627a8c1eMarco Nelissen
683957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang    private static final String AUDIO_COLUMNSv405 =
684957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "_data,_display_name,_size,mime_type,date_added,is_drm," +
685957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "date_modified,title,title_key,duration,artist_id,composer,album_id," +
686957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "track,year,is_ringtone,is_music,is_alarm,is_notification,is_podcast," +
687957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        "bookmark,album_artist";
688957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
689afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String VIDEO_COLUMNS =
690afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
691afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title,duration,artist,album,resolution,description,isprivate,tags," +
692afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
693bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "mini_thumb_magic,bucket_id,bucket_display_name,bookmark,width," +
694bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "height";
695bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
696bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang    private static final String VIDEO_COLUMNSv407 =
697bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "_data,_display_name,_size,mime_type,date_added,date_modified," +
698bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "title,duration,artist,album,resolution,description,isprivate,tags," +
699bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        "category,language,mini_thumb_data,latitude,longitude,datetaken," +
700afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic,bucket_id,bucket_display_name, bookmark";
701afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
702afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood    private static final String PLAYLIST_COLUMNS = "_data,name,date_added,date_modified";
703afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
71290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen    private static void updateDatabase(Context context, SQLiteDatabase db, boolean internal,
713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
71690c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        int dbversion = getDatabaseVersion(context);
71790c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (toVersion != dbversion) {
71890c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " + dbversion);
719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
72195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
725988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        long startTime = SystemClock.currentTimeMicro();
726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
727d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        // Revisions 84-86 were a failed attempt at supporting the "album artist" id3 tag.
728acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        // We can't downgrade from those revisions, so start over.
729022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // (the initial change to do this was wrong, so now we actually need to start over
730022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen        // if the database version is 84-89)
731bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Post-gingerbread, revisions 91-94 were broken in a way that is not easy to repair.
732bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // However version 91 was reused in a divergent development path for gingerbread,
733bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // so we need to support upgrades from 91.
734bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        // Therefore we will only force a reset for versions 92 - 94.
735d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (fromVersion < 63 || (fromVersion >= 84 && fromVersion <= 89) ||
736bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin                    (fromVersion >= 92 && fromVersion <= 94)) {
737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
740d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            fromVersion = 63;
741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
763cec8df8a90209fc4df5d1ff5f02dc364d0d2edc6Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
7649ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup");
7659ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup");
7669ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup");
7679ea338b3f2720a16a334990bf2bb6afc5011b60eMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup");
768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
798b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
818216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                       "_data TEXT UNIQUE NOT NULL," +
819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
85895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
864acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album) AS number_of_albums, " +
865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
878acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
879acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
880acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
881acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
882acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
90078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                // Contains mappings between audio genres and audio files
901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
991acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
992acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.0 shipped with database version 64
993acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
994acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1000403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // In version 66, originally we updateBucketNames(db, "images"),
1001403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen        // but we need to do it in version 89 and therefore save the update here.
1002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
1004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
1005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
1006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
1007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
1010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
1011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
1012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
1013403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1014403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // In version 68, originally we updateBucketNames(db, "video"),
1015403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen            // but we need to do it in version 89 and therefore save the update here.
1016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
1019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
1020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
1023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
1024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
1025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
102695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
1028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
1029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
1030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
1031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
1032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
1033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
1034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
1035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
1036e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "artist='" + MediaStore.UNKNOWN_STRING + "' AND " +
1037e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "album='" + MediaStore.UNKNOWN_STRING + "'" +
1038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
1039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
104095ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
1042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
1043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
1044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
1045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
1046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
1047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
1048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
1050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
1051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
1052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
1053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
1054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
105595ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1056acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1057acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.5 shipped with database version 72
1058acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1059acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
10608d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
10618d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
10628d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
10638d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
10648d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
10658d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
10668d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
10678d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
10688d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
10698d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
10708d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
10718d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
10728d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
10738d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
10748d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
1075a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1076a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
1077a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
1078a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
1079a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
1080a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
1081a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
1082a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
1083a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
1084a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
1085a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1086a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
1087a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1088a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
1089a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1090a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
1091a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
1092a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
1093a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
1094a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
1095a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
1096a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
1097e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " +
1098a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1099a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
1100a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
1101a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1102a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1103a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
1104a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
1105a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1106a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1107a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1108a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
1109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
1110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
1111e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " +
1112a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
1113a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
1114a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
1115a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
1116a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
1117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
1118a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
1119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
1120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
1121a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
1122a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
1123a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
1124a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
1125a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
1126a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
1127a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
1128a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
112959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
113059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
113195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
113259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
113359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
113459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
113559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
113615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
113715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
113815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
113915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
114015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
114115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
114215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
114315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
114415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
114515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
114615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
114715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
114815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
114915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
1150b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1151acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1152acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 1.6 shipped with database version 76
1153acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1154acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1155b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
1156b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
1157b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
1158b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
1159b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
1160b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
1161b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
1162b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
1163b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
1164b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
1165b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1166b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
1167b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1168b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
1169b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
1170b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
1171b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
1172b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
11731769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen
1174acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1175acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.0 and 2.0.1 shipped with database version 77
1176acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1177acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
11781769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        if (fromVersion < 78) {
1179044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force a rescan of the video entries so we can update
11801769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            // latest changed DATE_TAKEN units (in milliseconds).
11811769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            db.execSQL("UPDATE video SET date_modified=0;");
11821769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        }
1183268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
1184acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /*
1185acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         *  Android 2.1 shipped with database version 78
1186acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen         */
1187acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen
1188268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        if (fromVersion < 79) {
1189268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // move /sdcard/albumthumbs to
1190268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // /sdcard/Android/data/com.android.providers.media/albumthumbs,
1191268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // and update the database accordingly
1192268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
11939be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String oldthumbspath = mExternalStoragePaths[0] + "/albumthumbs";
11949be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String newthumbspath = mExternalStoragePaths[0] + "/" + ALBUM_THUMB_FOLDER;
1195268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            File thumbsfolder = new File(oldthumbspath);
1196268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            if (thumbsfolder.exists()) {
1197268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                // move folder to its new location
1198268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                File newthumbsfolder = new File(newthumbspath);
1199268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                newthumbsfolder.getParentFile().mkdirs();
1200268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                if(thumbsfolder.renameTo(newthumbsfolder)) {
1201268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    // update the database
1202268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" +
1203268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                            oldthumbspath + "','" + newthumbspath + "');");
1204268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                }
1205268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            }
1206268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        }
1207044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen
1208044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        if (fromVersion < 80) {
1209044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force rescan of image entries to update DATE_TAKEN as UTC timestamp.
1210044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            db.execSQL("UPDATE images SET date_modified=0;");
1211044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        }
12120ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
1213166a4e3cc66a645cc5e11d2f06d059512def0aceMarco Nelissen        if (fromVersion < 81 && !internal) {
12140ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete entries starting with /mnt/sdcard. This is for the benefit
12150ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // of users running builds between 2.0.1 and 2.1 final only, since
12160ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // users updating from 2.0 or earlier will not have such entries.
12170ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12180ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // First we need to update the _data fields in the affected tables, since
12190ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // otherwise deleting the entries will also delete the underlying files
12200ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // (via a trigger), and we want to keep them.
12210ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12220ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12230ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12240ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12250ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12260ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
1227216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("UPDATE audio_meta SET _data='////' WHERE _data LIKE '/mnt/sdcard/%';");
12280ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Once the paths have been renamed, we can safely delete the entries
12290ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_playlists WHERE _data IS '////';");
12300ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM images WHERE _data IS '////';");
12310ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM video WHERE _data IS '////';");
12320ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data IS '////';");
12330ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data IS '////';");
12340ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM audio_meta WHERE _data  IS '////';");
12350ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data  IS '////';");
12360ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12370ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // rename existing entries starting with /sdcard to /mnt/sdcard
12380ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta" +
12390ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12400ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_playlists" +
12410ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12420ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE images" +
12430ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12440ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE video" +
12450ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12460ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE videothumbnails" +
12470ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12480ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE thumbnails" +
12490ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12500ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE album_art" +
12510ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen                    " SET _data='/mnt/sdcard'||SUBSTR(_data,8) WHERE _data LIKE '/sdcard/%';");
12520ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen
12530ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // Delete albums and artists, then clear the modification time on songs, which
12540ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // will cause the media scanner to rescan everything, rebuilding the artist and
12550ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // album tables along the way, while preserving playlists.
12560ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // We need this rescan because ICU also changed, and now generates different
12570ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            // collation keys
12580ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from albums");
12590ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("DELETE from artists");
12600ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
12610ba6af212288010ca16b08f8be3985edb287cb0fMarco Nelissen        }
126284403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen
126384403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        if (fromVersion < 82) {
1264acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // recreate this view with the correct "group by" specifier
126584403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("DROP VIEW IF EXISTS artist_info");
126684403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
126784403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "SELECT artist_id AS _id, artist, artist_key, " +
1268acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "COUNT(DISTINCT album_key) AS number_of_albums, " +
126984403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
127084403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen                        "GROUP BY artist_key;");
127184403f9475c71ba53fa9bedfc9953d9f0ad0fc2cMarco Nelissen        }
1272216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1273acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        /* we skipped over version 83, and reverted versions 84, 85 and 86 */
1274ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen
1275ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        if (fromVersion < 87) {
1276ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // The fastscroll thumb needs an index on the strings being displayed,
1277ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // otherwise the queries it does to determine the correct position
1278ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen            // becomes really inefficient
1279022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS title_idx on audio_meta(title);");
1280022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_idx on artists(artist);");
1281022eb71bfe8ec65b8817eaa432a530194210ec23Marco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_idx on albums(album);");
1282ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen        }
1283216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen
1284acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        if (fromVersion < 88) {
1285acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // Clean up a few more things from versions 84/85/86, and recreate
1286acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // the few things worth keeping from those changes.
1287acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update1;");
1288acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update2;");
1289acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update3;");
1290acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS albums_update4;");
1291acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update1;");
1292acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update2;");
1293acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update3;");
1294acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS artist_update4;");
129516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP VIEW IF EXISTS album_artists;");
1296acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS album_id_idx on audio_meta(album_id);");
1297acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE INDEX IF NOT EXISTS artist_id_idx on audio_meta(artist_id);");
1298acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // For a given artist_id, provides the album_id for albums on
1299acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            // which the artist appears.
1300acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
1301acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
1302acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        }
1303403ebe07562bb44334724fde9749c22479204662Wei-Ta Chen
1304fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // In version 89, originally we updateBucketNames(db, "images") and
1305fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // updateBucketNames(db, "video"), but in version 101 we now updateBucketNames
1306fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        //  for all files and therefore can save the update here.
1307b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
1308b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (fromVersion < 91) {
1309bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // Never query by mini_thumb_magic_index
1310bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("DROP INDEX IF EXISTS mini_thumb_magic_index");
1311bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1312bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            // sort the items by taken date in each bucket
1313bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS image_bucket_index ON images(bucket_id, datetaken)");
1314bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin            db.execSQL("CREATE INDEX IF NOT EXISTS video_bucket_index ON video(bucket_id, datetaken)");
1315bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin        }
1316bdd3b8337b01920822c128b1ad1be202e22d070cOwen Lin
1317a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
1318d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // Gingerbread ended up going to version 100, but didn't yet have the "files"
1319d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // table, so we need to create that if we're at 100 or lower. This means
1320d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        // we won't be able to upgrade pre-release Honeycomb.
1321d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen        if (fromVersion <= 100) {
1322afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Remove various stages of work in progress for MTP support
1323afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS objects");
1324afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS files");
132516dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_objects_cleanup;");
132616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_objects_cleanup;");
132716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_objects_cleanup;");
132816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS playlists_objects_cleanup;");
1329afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_images;");
1330afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_audio;");
1331afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_video;");
1332afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup_playlists;");
1333afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS media_cleanup;");
1334afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1335afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create a new table to manage all files in our storage.
1336afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // This contains a union of all the columns from the old
1337afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // images, audio_meta, videos and audio_playlist tables.
1338afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TABLE files (" +
133900a22306f6c99d1f1b4424f8f6a1cad8fb332d85Ray Chen                        "_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1340afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_data TEXT," +     // this can be null for playlists
1341afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_size INTEGER," +
1342afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "format INTEGER," +
1343afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "parent INTEGER," +
1344afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_added INTEGER," +
1345afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "date_modified INTEGER," +
1346afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mime_type TEXT," +
1347afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title TEXT," +
1348afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "description TEXT," +
1349afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "_display_name TEXT," +
1350afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1351afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images
1352afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "picasa_id TEXT," +
1353afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "orientation INTEGER," +
1354afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1355afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for images and video
1356afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "latitude DOUBLE," +
1357afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "longitude DOUBLE," +
1358afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "datetaken INTEGER," +
1359afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_magic INTEGER," +
1360afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_id TEXT," +
1361afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bucket_display_name TEXT," +
1362afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "isprivate INTEGER," +
1363afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1364afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio
1365afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "title_key TEXT," +
1366afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist_id INTEGER," +
1367afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album_id INTEGER," +
1368afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "composer TEXT," +
1369afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "track INTEGER," +
1370afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "year INTEGER CHECK(year!=0)," +
1371afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_ringtone INTEGER," +
1372afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_music INTEGER," +
1373afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_alarm INTEGER," +
1374afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_notification INTEGER," +
1375afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "is_podcast INTEGER," +
1376d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        "album_artist TEXT," +
1377afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1378afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for audio and video
1379afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "duration INTEGER," +
1380afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "bookmark INTEGER," +
1381afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1382afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for video
1383afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "artist TEXT," +
1384afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "album TEXT," +
1385afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "resolution TEXT," +
1386afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "tags TEXT," +
1387afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "category TEXT," +
1388afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "language TEXT," +
1389afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "mini_thumb_data TEXT," +
1390afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1391afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // for playlists
1392afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "name TEXT," +
1393afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1394afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // media_type is used by the views to emulate the old
1395afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // images, audio_meta, videos and audio_playlist tables.
1396afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "media_type INTEGER," +
1397afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1398afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Value of _id from the old media table.
1399afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        // Used only for updating other tables during database upgrade.
1400afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "old_id INTEGER" +
1401afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       ");");
1402d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen
1403afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX path_index ON files(_data);");
1404afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1405afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1406afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Copy all data from our obsolete tables to the new files table
140792be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
140892be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Copy audio records first, preserving the _id column.
140992be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We do this to maintain compatibility for content Uris for ringtones.
141092be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // Unfortunately we cannot do this for images and videos as well.
141192be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // We choose to do this for the audio table because the fragility of Uris
141292be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            // for ringtones are the most common problem we need to avoid.
141392be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood            db.execSQL("INSERT INTO files (_id," + AUDIO_COLUMNSv99 + ",old_id,media_type)" +
141492be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " SELECT _id," + AUDIO_COLUMNSv99 + ",_id," + FileColumns.MEDIA_TYPE_AUDIO +
141592be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood                    " FROM audio_meta;");
141692be385568284e6351ab49f5799eca0e35b9d98dMike Lockwood
1417bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + IMAGE_COLUMNSv407 + ",old_id,media_type) SELECT "
1418bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + IMAGE_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_IMAGE + " FROM images;");
1419bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("INSERT INTO files (" + VIDEO_COLUMNSv407 + ",old_id,media_type) SELECT "
1420bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                    + VIDEO_COLUMNSv407 + ",_id," + FileColumns.MEDIA_TYPE_VIDEO + " FROM video;");
142116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            if (!internal) {
1422afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("INSERT INTO files (" + PLAYLIST_COLUMNS + ",old_id,media_type) SELECT "
1423afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + PLAYLIST_COLUMNS + ",_id," + FileColumns.MEDIA_TYPE_PLAYLIST
1424afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + " FROM audio_playlists;");
1425afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
142616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1427afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Delete the old tables
1428afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS images");
1429afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_meta");
1430afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS video");
1431afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
143216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood
1433afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Create views to replace our old tables
1434bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNSv407 +
1435afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1436afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1437d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv100 +
1438d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1439d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1440bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNSv407 +
1441afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1442afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1443afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1444afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE VIEW audio_playlists AS SELECT _id," + PLAYLIST_COLUMNS +
1445afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1446afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_PLAYLIST + ";");
144716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            }
144836339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
14499491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // create temporary index to make the updates go faster
14509491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("CREATE INDEX tmp ON files(old_id);");
14519491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1452afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update the image_id column in the thumbnails table.
1453afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE thumbnails SET image_id = (SELECT _id FROM files "
1454afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = thumbnails.image_id AND files.media_type = "
1455afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_IMAGE + ");");
145636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1457afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1458d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // update audio_id in the audio_genres_map table, and
1459d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                // audio_playlists_map tables and playlist_id in the audio_playlists_map table
1460afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_genres_map SET audio_id = (SELECT _id FROM files "
1461afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_genres_map.audio_id AND files.media_type = "
1462afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_AUDIO + ");");
1463afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("UPDATE audio_playlists_map SET audio_id = (SELECT _id FROM files "
1464afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = audio_playlists_map.audio_id "
1465afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + ");");
1466d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET playlist_id = (SELECT _id FROM files "
1467d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "WHERE files.old_id = audio_playlists_map.playlist_id "
1468d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        + "AND files.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + ");");
1469afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
1470afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1471afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update video_id in the videothumbnails table.
1472afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("UPDATE videothumbnails SET video_id = (SELECT _id FROM files "
1473afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + "WHERE files.old_id = videothumbnails.video_id AND files.media_type = "
1474afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        + FileColumns.MEDIA_TYPE_VIDEO + ");");
1475afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
14769491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            // we don't need this index anymore now
14779491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen            db.execSQL("DROP INDEX tmp;");
14789491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen
1479afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // update indices to work on the files table
1480afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS title_idx");
1481afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS album_id_idx");
1482afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS image_bucket_index");
1483afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS video_bucket_index");
1484afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS sort_index");
1485afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS titlekey_index");
1486afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP INDEX IF EXISTS artist_id_idx");
1487afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX title_idx ON files(title);");
1488afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1489afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX bucket_index ON files(bucket_id, datetaken);");
1490afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1491afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1492afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1493afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1494afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            // Recreate triggers for our obsolete tables on the new files table
1495afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
1496afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
1497afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
1498afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
1499afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
150036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1501afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON files " +
1502afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_IMAGE + " " +
150336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1504afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
1505afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
150636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
150736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1508afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON files " +
150949dea76284f7693ba452c05cfd59c1d9c9584343Ray Chen                    "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_VIDEO + " " +
151036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "BEGIN " +
1511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        "SELECT _DELETE_FILE(old._data);" +
151236339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    "END");
151336339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1514afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (!internal) {
1515afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON files " +
1516afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_AUDIO + " " +
1517afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1518afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
1519afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
1520afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1521afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1522afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON files " +
1523afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "WHEN old.media_type = " + FileColumns.MEDIA_TYPE_PLAYLIST + " " +
1524afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "BEGIN " +
1525afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1526afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                           "SELECT _DELETE_FILE(old._data);" +
1527afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                       "END");
1528afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
1529afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
153036339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "BEGIN " +
1531afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from files where _id=old._id;" +
1532afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_playlists_map where audio_id=old._id;" +
1533afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "DELETE from audio_genres_map where audio_id=old._id;" +
153436339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        "END");
153536339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood            }
153636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood        }
153736339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood
1538db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (fromVersion < 301) {
1539db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("DROP INDEX IF EXISTS bucket_index");
1540db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_index on files(bucket_id, media_type, datetaken, _id)");
1541db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            db.execSQL("CREATE INDEX bucket_name on files(bucket_id, media_type, bucket_display_name)");
1542db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
1543db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin
154420405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        if (fromVersion < 302) {
154520405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX parent_index ON files(parent);");
154620405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood            db.execSQL("CREATE INDEX format_index ON files(format);");
154720405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood        }
154820405fab4cf752dbdec6f1020bfc70a2bf87590eMike Lockwood
15492658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        if (fromVersion < 303) {
15502658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // the album disambiguator hash changed, so rescan songs and force
15512658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            // albums to be updated. Artists are unaffected.
15522658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("DELETE from albums");
15532658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
15542658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
15552658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        }
15562658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
15574b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 304 && !internal) {
155851d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            // notifies host when files are deleted
155951d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood            db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
156051d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "BEGIN " +
156151d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                        "SELECT _OBJECT_REMOVED(old._id);" +
156251d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood                    "END");
156351d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
156451d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood        }
156551d6c5d233f78de2db9b3f63514b20b1ce768dd5Mike Lockwood
15664b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        if (fromVersion < 305 && internal) {
15674b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            // version 304 erroneously added this trigger to the internal database
15684b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
15694b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood        }
15704b5963f15c1563c613ec505ca7962f93aec44321Mike Lockwood
1571fda522dc66b94057f9c6676cb8ba10bc3b13daeaMarco Nelissen        if (fromVersion < 306 && !internal) {
1572efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // The genre list was expanded and genre string parsing was tweaked, so
1573efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            // rebuild the genre list
1574efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
1575efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
1576efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
1577efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
1578efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen        }
1579efe0bcdf5ed215a5cb76c8a48aad1333056636f4Marco Nelissen
158065587f9c204abb2d179527dfdca009f4780e9743Ray Chen        if (fromVersion < 307 && !internal) {
158165587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // Force rescan of image entries to update DATE_TAKEN by either GPSTimeStamp or
158265587f9c204abb2d179527dfdca009f4780e9743Ray Chen            // EXIF local time.
158365587f9c204abb2d179527dfdca009f4780e9743Ray Chen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
158465587f9c204abb2d179527dfdca009f4780e9743Ray Chen                    + FileColumns.MEDIA_TYPE_IMAGE + ";");
158565587f9c204abb2d179527dfdca009f4780e9743Ray Chen        }
158665587f9c204abb2d179527dfdca009f4780e9743Ray Chen
1587e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // Honeycomb went up to version 307, ICS started at 401
1588e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
15894f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // Database version 401 did not add storage_id to the internal database.
15904f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        // We need it there too, so add it in version 402
15914f63f7c1e6f602a77abd43b189f296b9eb36635bMike Lockwood        if (fromVersion < 401 || (fromVersion == 401 && internal)) {
15929be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Add column for MTP storage ID
15939be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("ALTER TABLE files ADD COLUMN storage_id INTEGER;");
15949be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            // Anything in the database before this upgrade step will be in the primary storage
15959be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            db.execSQL("UPDATE files SET storage_id=" + MtpStorage.getStorageId(0) + ";");
15969be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
15979be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
159878b2885edc406273d688536b0eadfea006b20662Marco Nelissen        if (fromVersion < 403 && !internal) {
159978b2885edc406273d688536b0eadfea006b20662Marco Nelissen            db.execSQL("CREATE VIEW audio_genres_map_noid AS " +
160078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    "SELECT audio_id,genre_id from audio_genres_map;");
160178b2885edc406273d688536b0eadfea006b20662Marco Nelissen        }
160278b2885edc406273d688536b0eadfea006b20662Marco Nelissen
16039289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        if (fromVersion < 404) {
16049289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // There was a bug that could cause distinct same-named albums to be
16059289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            // combined again. Delete albums and force a rescan.
16069289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("DELETE from albums");
16079289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16089289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
16099289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen        }
16109289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen
1611957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        if (fromVersion < 405) {
1612957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            // Add is_drm column.
1613957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("ALTER TABLE files ADD COLUMN is_drm INTEGER;");
1614957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1615957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("DROP VIEW IF EXISTS audio_meta");
1616957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            db.execSQL("CREATE VIEW audio_meta AS SELECT _id," + AUDIO_COLUMNSv405 +
1617957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1618957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang                        + FileColumns.MEDIA_TYPE_AUDIO + ";");
1619957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1620957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang            recreateAudioView(db);
1621957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang        }
1622957002d42eec79c6f7ce11c98483d7aa3f224e51Gloria Wang
1623b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (fromVersion < 407) {
16247ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            // Rescan files in the media database because a new column has been added
1625b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // in table files in version 405 and to recover from problems populating
1626b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // the genre tables
16277ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang            db.execSQL("UPDATE files SET date_modified=0;");
16287ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang        }
16297ee82723c70fc02b168cfa9b6ff7d5fdd0c16d53Gloria Wang
1630bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        if (fromVersion < 408) {
1631bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Add the width/height columns for images and video
1632bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN width INTEGER;");
1633bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("ALTER TABLE files ADD COLUMN height INTEGER;");
1634bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1635bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Rescan files to fill the columns
1636bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("UPDATE files SET date_modified=0;");
1637bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
1638bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            // Update images and video views to contain the width/height columns
1639bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS images");
1640bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("DROP VIEW IF EXISTS video");
1641bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW images AS SELECT _id," + IMAGE_COLUMNS +
1642bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1643bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_IMAGE + ";");
1644bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang            db.execSQL("CREATE VIEW video AS SELECT _id," + VIDEO_COLUMNS +
1645bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        " FROM files WHERE " + FileColumns.MEDIA_TYPE + "="
1646bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang                        + FileColumns.MEDIA_TYPE_VIDEO + ";");
1647bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang        }
1648bdcd6c157b84f26bd006188c18c8c07a543afe8cChih-Chung Chang
16495809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        if (fromVersion < 409 && !internal) {
16505809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // A bug that prevented numeric genres from being parsed was fixed, so
16515809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            // rebuild the genre list
16525809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16535809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen                    + FileColumns.MEDIA_TYPE_AUDIO + ";");
16545809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres_map");
16555809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen            db.execSQL("DELETE FROM audio_genres");
16565809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen        }
16575809eb2b5fd77bd7d14548669c70e4a672e43df6Marco Nelissen
1658e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // ICS went out with database version 409, JB started at 500
1659e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1660166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        if (fromVersion < 500) {
1661166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1662166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS videothumbnails_cleanup;");
1663166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen        }
1664a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        if (fromVersion < 501) {
1665a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
1666a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // the images_cleanup trigger would delete the image file and the entry
1667a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // in the thumbnail table, which in turn would trigger thumbnails_cleanup
1668a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            // to delete the thumbnail image
1669a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup;");
1670a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup;");
1671a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        }
16725afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        if (fromVersion < 502) {
16735afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            // we're now deleting the file in mediaprovider code, rather than via a trigger
16745afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup;");
16755afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen        }
16765118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        if (fromVersion < 503) {
16775118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            // genre and playlist cleanup now done in mediaprovider code, instead of in a trigger
16785118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
16795118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
16805118dba282c95fda77e03e63b6dd11505c474ee5Marco Nelissen        }
168190c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        if (fromVersion < 504) {
168290c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            // add an index to help with case-insensitive matching of paths
168390c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            db.execSQL(
168490c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen                    "CREATE INDEX IF NOT EXISTS path_index_lower ON files(_data COLLATE NOCASE);");
168590c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen        }
16867074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen        if (fromVersion < 505) {
16877074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            // Starting with schema 505 we fill in the width/height/resolution columns for videos,
16887074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            // so force a rescan of videos to fill in the blanks
16897074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen            db.execSQL("UPDATE files SET date_modified=0 WHERE " + FileColumns.MEDIA_TYPE + "="
16907074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen                    + FileColumns.MEDIA_TYPE_VIDEO + ";");
16917074cb9b72666fc300605abe5fa4d28f7f9f4c0dMarco Nelissen        }
169259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen        if (fromVersion < 506) {
169359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // sd card storage got moved to /storage/sdcard0
169459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // first delete everything that already got scanned in /storage before this
169559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // update step was added
169659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DROP TRIGGER IF EXISTS files_cleanup");
169759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM files WHERE _data LIKE '/storage/%';");
169859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM album_art WHERE _data LIKE '/storage/%';");
169959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM thumbnails WHERE _data LIKE '/storage/%';");
170059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("DELETE FROM videothumbnails WHERE _data LIKE '/storage/%';");
170159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // then rename everything from /mnt/sdcard/ to /storage/sdcard0,
170259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            // and from /mnt/external1 to /storage/sdcard1
170359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE files SET " +
170459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
170559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE files SET " +
170659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
170759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE album_art SET " +
170859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
170959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE album_art SET " +
171059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
171159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE thumbnails SET " +
171259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
171359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE thumbnails SET " +
171459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
171559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE videothumbnails SET " +
171659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard0'||SUBSTR(_data,12) WHERE _data LIKE '/mnt/sdcard/%';");
171759f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            db.execSQL("UPDATE videothumbnails SET " +
171859f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                "_data='/storage/sdcard1'||SUBSTR(_data,15) WHERE _data LIKE '/mnt/external1/%';");
171959f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen
172059f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            if (!internal) {
172159f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                db.execSQL("CREATE TRIGGER IF NOT EXISTS files_cleanup DELETE ON files " +
172259f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                    "BEGIN " +
172359f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                        "SELECT _OBJECT_REMOVED(old._id);" +
172459f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen                    "END");
172559f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen            }
172659f8f11eaef252b82d62bfd5e763ccf350aa48b6Marco Nelissen        }
1727a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin        if (fromVersion < 507) {
1728a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin            // we update _data in version 506, we need to update the bucket_id as well
1729677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            updateBucketNames(db);
1730a2466a7a8613b61fa570a4e68bff9460c1ab1920Owen Lin        }
173146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen        if (fromVersion < 508 && !internal) {
173246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            // ensure we don't get duplicate entries in the genre map
173346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map_tmp (" +
173446e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "_id INTEGER PRIMARY KEY," +
173546e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "audio_id INTEGER NOT NULL," +
173646e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "genre_id INTEGER NOT NULL," +
173746e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    "UNIQUE (audio_id,genre_id) ON CONFLICT IGNORE" +
173846e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    ");");
173946e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("INSERT INTO audio_genres_map_tmp (audio_id,genre_id)" +
174046e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen                    " SELECT DISTINCT audio_id,genre_id FROM audio_genres_map;");
174146e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("DROP TABLE audio_genres_map;");
174246e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen            db.execSQL("ALTER TABLE audio_genres_map_tmp RENAME TO audio_genres_map;");
174346e56db35b1ac30062db31f38b04cb719bba6bd8Marco Nelissen        }
1744988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1745988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        if (fromVersion < 509) {
1746988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS log (time DATETIME PRIMARY KEY, message TEXT);");
1747988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        }
1748395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1749395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        // Emulated external storage moved to user-specific paths
1750395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        if (fromVersion < 510 && Environment.isExternalStorageEmulated()) {
1751395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            // File.fixSlashes() removes any trailing slashes
1752395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            final String externalStorage = Environment.getExternalStorageDirectory().toString();
175357b65f10aaafc4bf42a0fa59eb2bbe6c8371c2e6Jeff Sharkey            Log.d(TAG, "Adjusting external storage paths to: " + externalStorage);
1754395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1755395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            final String[] tables = {
1756395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                    TABLE_FILES, TABLE_ALBUM_ART, TABLE_THUMBNAILS, TABLE_VIDEO_THUMBNAILS };
1757395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            for (String table : tables) {
1758395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                db.execSQL("UPDATE " + table + " SET " + "_data='" + externalStorage
1759395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey                        + "'||SUBSTR(_data,17) WHERE _data LIKE '/storage/sdcard0/%';");
1760395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey            }
1761395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey        }
17628bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey        if (fromVersion < 511) {
17638bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey            // we update _data in version 510, we need to update the bucket_id as well
17648bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey            updateBucketNames(db);
17658bd4260d1b6c02252130f8c319ca6f4c3aed6297Jeff Sharkey        }
1766395ad2597601ccf285cfe679c7263a1b1feee231Jeff Sharkey
1767e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        // JB 4.2 went out with database version 511, starting next release with 600
1768e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1769e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        if (fromVersion < 600) {
1770e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // modify _data column to be unique and collate nocase. Because this drops the original
1771e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // table and replaces it with a new one by the same name, we need to also recreate all
1772e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // indices and triggers that refer to the files table.
1773e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // Views don't need to be recreated.
1774e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1775e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE TABLE files2 (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
1776e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_data TEXT UNIQUE" +
1777e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    // the internal filesystem is case-sensitive
1778e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    (internal ? "," : " COLLATE NOCASE,") +
1779e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_size INTEGER,format INTEGER,parent INTEGER,date_added INTEGER," +
1780e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "date_modified INTEGER,mime_type TEXT,title TEXT,description TEXT," +
1781e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "_display_name TEXT,picasa_id TEXT,orientation INTEGER,latitude DOUBLE," +
1782e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER,bucket_id TEXT," +
1783e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "bucket_display_name TEXT,isprivate INTEGER,title_key TEXT,artist_id INTEGER," +
1784e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "album_id INTEGER,composer TEXT,track INTEGER,year INTEGER CHECK(year!=0)," +
1785e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "is_ringtone INTEGER,is_music INTEGER,is_alarm INTEGER," +
1786e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "is_notification INTEGER,is_podcast INTEGER,album_artist TEXT," +
1787e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "duration INTEGER,bookmark INTEGER,artist TEXT,album TEXT,resolution TEXT," +
1788e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "tags TEXT,category TEXT,language TEXT,mini_thumb_data TEXT,name TEXT," +
1789e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "media_type INTEGER,old_id INTEGER,storage_id INTEGER,is_drm INTEGER," +
1790e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "width INTEGER, height INTEGER);");
1791e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1792e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // copy data from old table, squashing entries with duplicate _data
1793e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("INSERT OR REPLACE INTO files2 SELECT * FROM files;");
1794e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("DROP TABLE files;");
1795e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("ALTER TABLE files2 RENAME TO files;");
1796e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1797e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            // recreate indices and triggers
1798e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX album_id_idx ON files(album_id);");
1799e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX artist_id_idx ON files(artist_id);");
1800e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX bucket_index on files(bucket_id,media_type," +
1801e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "datetaken, _id);");
1802e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX bucket_name on files(bucket_id,media_type," +
1803e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                    "bucket_display_name);");
1804e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX format_index ON files(format);");
1805e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX media_type_index ON files(media_type);");
1806e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX parent_index ON files(parent);");
1807e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX path_index ON files(_data);");
1808e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);");
1809e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX title_idx ON files(title);");
1810e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            db.execSQL("CREATE INDEX titlekey_index ON files(title_key);");
1811e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            if (!internal) {
1812e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                db.execSQL("CREATE TRIGGER audio_playlists_cleanup DELETE ON files" +
1813e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " WHEN old.media_type=4" +
1814e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " BEGIN DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
1815e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        "SELECT _DELETE_FILE(old._data);END;");
1816e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                db.execSQL("CREATE TRIGGER files_cleanup DELETE ON files" +
1817e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen                        " BEGIN SELECT _OBJECT_REMOVED(old._id);END;");
1818e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            }
1819e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen        }
1820e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen
1821f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen        if (fromVersion < 601) {
1822f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            // remove primary key constraint because column time is not necessarily unique
1823f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("CREATE TABLE IF NOT EXISTS log_tmp (time DATETIME, message TEXT);");
1824f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("DELETE FROM log_tmp;");
1825f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("INSERT INTO log_tmp SELECT time, message FROM log order by rowid;");
1826f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("DROP TABLE log;");
1827f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen            db.execSQL("ALTER TABLE log_tmp RENAME TO log;");
1828f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen        }
1829f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen
18302a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen        if (fromVersion < 700) {
18312a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // fix datetaken fields that were added with an incorrect timestamp
18322a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // datetaken needs to be in milliseconds, so should generally be a few orders of
18332a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // magnitude larger than date_modified. If it's within the same order of magnitude, it
18342a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // is probably wrong.
18352a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            // (this could do the wrong thing if your picture was actually taken before ~3/21/1970)
18362a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen            db.execSQL("UPDATE files set datetaken=date_modified*1000"
18372a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen                    + " WHERE date_modified IS NOT NULL"
18382a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen                    + " AND datetaken IS NOT NULL"
18392a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen                    + " AND datetaken<date_modified*5;");
18402a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen        }
18412a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen
18422a83ace1b13b45c048c5315dab506842e99e080cMarco Nelissen
1843acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen        sanityCheck(db, fromVersion);
1844988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        long elapsedSeconds = (SystemClock.currentTimeMicro() - startTime) / 1000000;
1845988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        logToDb(db, "Database upgraded from version " + fromVersion + " to " + toVersion
1846988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                + " in " + elapsedSeconds + " seconds");
1847988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    }
1848988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1849988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    /**
1850988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen     * Write a persistent diagnostic message to the log table.
1851988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen     */
1852988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    static void logToDb(SQLiteDatabase db, String message) {
1853934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen        db.execSQL("INSERT OR REPLACE" +
1854934af0229b25901e61a030f17bf220722ccfb427Marco Nelissen                " INTO log (time,message) VALUES (strftime('%Y-%m-%d %H:%M:%f','now'),?);",
1855988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                new String[] { message });
1856988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        // delete all but the last 500 rows
1857988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        db.execSQL("DELETE FROM log WHERE rowid IN" +
1858f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen                " (SELECT rowid FROM log ORDER BY rowid DESC LIMIT 500,-1);");
18591d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    }
18601d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen
18611d4a8ec9e1e62427088dc40f08872a10c863535eMarco Nelissen    /**
1862216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * Perform a simple sanity check on the database. Currently this tests
1863216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     * whether all the _data entries in audio_meta are unique
1864216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen     */
1865216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen    private static void sanityCheck(SQLiteDatabase db, int fromVersion) {
1866216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c1 = db.query("audio_meta", new String[] {"count(*)"},
1867216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1868216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        Cursor c2 = db.query("audio_meta", new String[] {"count(distinct _data)"},
1869216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                null, null, null, null, null);
1870216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.moveToFirst();
1871216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.moveToFirst();
1872216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num1 = c1.getInt(0);
1873216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        int num2 = c2.getInt(0);
1874216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c1.close();
1875216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        c2.close();
1876216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        if (num1 != num2) {
1877216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            Log.e(TAG, "audio_meta._data column is not unique while upgrading" +
1878216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                    " from schema " +fromVersion + " : " + num1 +"/" + num2);
1879216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            // Delete all audio_meta rows so they will be rebuilt by the media scanner
1880216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen            db.execSQL("DELETE FROM audio_meta;");
1881216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen        }
1882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
1885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
1886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
1887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
1888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
1889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
1890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
189195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
1892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1893677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen     * Update the bucket_id and bucket_display_name columns for images and videos
1894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1897677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen    private static void updateBucketNames(SQLiteDatabase db) {
1898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
1899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
1902677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            // update only images and videos
1903677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen            Cursor cursor = db.query("files", columns, "media_type=1 OR media_type=3",
1904677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen                    null, null, null, null);
1905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
19089491c3bd011644f5fe69fd47f73d0e1245b33febMarco Nelissen                String [] rowId = new String[1];
1909988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                ContentValues values = new ContentValues();
1910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
1912988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    rowId[0] = cursor.getString(idColumnIndex);
1913d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    if (data != null) {
1914988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        values.clear();
1915d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        computeBucketValues(data, values);
1916677d5c9a353fd956c9cb981ca38d9fb351c0421dMarco Nelissen                        db.update("files", values, "_id=?", rowId);
1917d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    } else {
1918d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                        Log.w(TAG, "null data at id " + rowId);
1919d6dc8dc70ae177c37be1c63ce6ebd97f27c44bd8Marco Nelissen                    }
1920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
1932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
1933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
1934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
1935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
1937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
1938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
1941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
1942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
1944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
1947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
1948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
1949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
1953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
1954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
1955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
1958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
1959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1965988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
1966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
1969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
1972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
1973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
1974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
1975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
1978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
1979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
1980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
1981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
1982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
1984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
1985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
1986d0d809c65db7d4936266c8f6a18511046c84fd15Mike Lockwood        values.put(ImageColumns.BUCKET_ID, path.hashCode());
1987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
1988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
1992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
1993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
1996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
1997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
1998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
1999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
2000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
2002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2004b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
2005498b62c2912302a23532c73a028a7684c5df33caRay Chen     * Copy taken time from date_modified if we lost the original value (e.g. after factory reset)
2006498b62c2912302a23532c73a028a7684c5df33caRay Chen     * This works for both video and image tables.
2007b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     *
2008b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     * @param values the content values, where taken time is updated.
2009b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen     */
2010b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    private static void computeTakenTime(ContentValues values) {
2011b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        if (! values.containsKey(Images.Media.DATE_TAKEN)) {
2012b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // This only happens when MediaScanner finds an image file that doesn't have any useful
2013b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            // reference to get this value. (e.g. GPSTimeStamp)
2014498b62c2912302a23532c73a028a7684c5df33caRay Chen            Long lastModified = values.getAsLong(MediaColumns.DATE_MODIFIED);
2015b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            if (lastModified != null) {
2016b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                values.put(Images.Media.DATE_TAKEN, lastModified * 1000);
2017b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen            }
2018b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen        }
2019b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    }
2020b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen
2021b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen    /**
2022b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
2023b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
2024b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
2025b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
2026b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
2027b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
2028b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
2029b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
2030b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c == null) return false;
2031b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2032b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
2033b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2034b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c.moveToFirst()) {
2035e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen            long id = c.getLong(0);
2036b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String path = c.getString(1);
2037b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            long magic = c.getLong(2);
2038b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
20399299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            MediaThumbRequest req = requestMediaThumbnail(path, origUri,
20409299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    MediaThumbRequest.PRIORITY_HIGH, magic);
20419299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            if (req == null) {
20429299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                return false;
20439299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            }
20449299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            synchronized (req) {
20459299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                try {
20469299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    while (req.mState == MediaThumbRequest.State.WAIT) {
20479299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        req.wait();
204820434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
20499299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                } catch (InterruptedException e) {
20509299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    Log.w(TAG, e);
20519299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                }
20529299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                if (req.mState == MediaThumbRequest.State.DONE) {
20539299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    result = true;
2054b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2055b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
2056b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2057b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        c.close();
2058b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2059b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
2060b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
2061b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2062e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid,
2063e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo) {
2064e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllOrigId = (id == -1);
2065e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllGroupId = (gid == -1);
2066e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        return (req.mCallingPid == pid) &&
2067e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllGroupId || req.mGroupId == gid) &&
2068e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllOrigId || req.mOrigId == id) &&
2069e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (req.mIsVideo == isVideo);
2070e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    }
2071e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
2072b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
2073b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
2074b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
2075b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
2076b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
2077b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
2078b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
2079b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
2080b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
2081b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2082b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
2083b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
2084b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
2085b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
2086b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
2087b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2088b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2089b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
209020434e032e498b716f87cce2f23dd646819218bfRay Chen        boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel"));
2091e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        Uri origUri = uri.buildUpon().encodedPath(
2092e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                uri.getPath().replaceFirst("thumbnails", "media"))
2093e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                .appendPath(origId).build();
2094b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2095b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
209620434e032e498b716f87cce2f23dd646819218bfRay Chen            Log.w(TAG, "original media doesn't exist or it's canceled.");
2097b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
209820434e032e498b716f87cce2f23dd646819218bfRay Chen        } else if (cancelRequest) {
2099e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            String groupId = uri.getQueryParameter("group_id");
2100e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo = "video".equals(uri.getPathSegments().get(1));
210120434e032e498b716f87cce2f23dd646819218bfRay Chen            int pid = Binder.getCallingPid();
210220434e032e498b716f87cce2f23dd646819218bfRay Chen            long id = -1;
2103e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            long gid = -1;
2104e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
210520434e032e498b716f87cce2f23dd646819218bfRay Chen            try {
210620434e032e498b716f87cce2f23dd646819218bfRay Chen                id = Long.parseLong(origId);
2107e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                gid = Long.parseLong(groupId);
210820434e032e498b716f87cce2f23dd646819218bfRay Chen            } catch (NumberFormatException ex) {
210920434e032e498b716f87cce2f23dd646819218bfRay Chen                // invalid cancel request
211020434e032e498b716f87cce2f23dd646819218bfRay Chen                return false;
211120434e032e498b716f87cce2f23dd646819218bfRay Chen            }
2112e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
211320434e032e498b716f87cce2f23dd646819218bfRay Chen            synchronized (mMediaThumbQueue) {
2114e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                if (mCurrentThumbRequest != null &&
2115e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                        matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) {
211620434e032e498b716f87cce2f23dd646819218bfRay Chen                    synchronized (mCurrentThumbRequest) {
211720434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL;
211820434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.notifyAll();
211920434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
212020434e032e498b716f87cce2f23dd646819218bfRay Chen                }
212120434e032e498b716f87cce2f23dd646819218bfRay Chen                for (MediaThumbRequest mtq : mMediaThumbQueue) {
2122e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                    if (matchThumbRequest(mtq, pid, id, gid, isVideo)) {
212320434e032e498b716f87cce2f23dd646819218bfRay Chen                        synchronized (mtq) {
212420434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.mState = MediaThumbRequest.State.CANCEL;
212520434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.notifyAll();
212620434e032e498b716f87cce2f23dd646819218bfRay Chen                        }
212720434e032e498b716f87cce2f23dd646819218bfRay Chen
212820434e032e498b716f87cce2f23dd646819218bfRay Chen                        mMediaThumbQueue.remove(mtq);
212920434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
213020434e032e498b716f87cce2f23dd646819218bfRay Chen                }
213120434e032e498b716f87cce2f23dd646819218bfRay Chen            }
2132b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2133b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2134b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
2135b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
2136b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
2137b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
2138b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
213901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
214001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    @Override
214101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    public Uri canonicalize(Uri uri) {
214201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        int match = URI_MATCHER.match(uri);
214301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
214401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        // only support canonicalizing specific audio Uris
214501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (match != AUDIO_MEDIA_ID) {
214601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return null;
214701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
214801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
214901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        Cursor c = query(uri, null, null, null, null);
215001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (c == null) {
215101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return null;
215201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
215301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (c.getCount() != 1 || !c.moveToNext()) {
215401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            c.close();
215501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return null;
215601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
215701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
215801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        // Construct a canonical Uri by tacking on some query parameters
215901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        Uri.Builder builder = uri.buildUpon();
216001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        builder.appendQueryParameter(CANONICAL, "1");
216101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        String title = c.getString(c.getColumnIndex(MediaStore.Audio.Media.TITLE));
216201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        c.close();
216301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (TextUtils.isEmpty(title)) {
216401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return null;
216501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
216601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        builder.appendQueryParameter(MediaStore.Audio.Media.TITLE, title);
216701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        Uri newUri = builder.build();
216801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        return newUri;
216901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    }
217001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
217101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    @Override
217201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    public Uri uncanonicalize(Uri uri) {
217301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (uri != null && "1".equals(uri.getQueryParameter(CANONICAL))) {
217401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            int match = URI_MATCHER.match(uri);
217501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            if (match != AUDIO_MEDIA_ID) {
217601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                // this type of canonical Uri is not supported
217701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                return null;
217801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            }
217901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            String titleFromUri = uri.getQueryParameter(MediaStore.Audio.Media.TITLE);
218001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            if (titleFromUri == null) {
218101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                // the required parameter is missing
218201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                return null;
218301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            }
218401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            // clear the query parameters, we don't need them anymore
218501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            uri = uri.buildUpon().clearQuery().build();
218601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
218701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            Cursor c = query(uri, null, null, null, null);
218801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
218901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            int titleIdx = c.getColumnIndex(MediaStore.Audio.Media.TITLE);
219001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            if (c != null && c.getCount() == 1 && c.moveToNext() &&
219101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                    titleFromUri.equals(c.getString(titleIdx))) {
219201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                // the result matched perfectly
219301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                c.close();
219401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                return uri;
219501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            }
219601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
219701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            c.close();
219801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            // do a lookup by title
219901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            Uri newUri = MediaStore.Audio.Media.getContentUri(uri.getPathSegments().get(0));
220001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
220101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            c = query(newUri, null, MediaStore.Audio.Media.TITLE + "=?",
220201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                    new String[] {titleFromUri}, null);
220301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            if (c == null) {
220401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                return null;
220501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            }
220601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            if (!c.moveToNext()) {
220701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                c.close();
220801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen                return null;
220901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            }
221001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            // get the first matching entry and return a Uri for it
221101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            long id = c.getLong(c.getColumnIndex(MediaStore.Audio.Media._ID));
221201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            c.close();
221301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return ContentUris.withAppendedId(newUri, id);
221401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
221501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        return uri;
221601e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    }
221701e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
221801e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    private Uri safeUncanonicalize(Uri uri) {
221901e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        Uri newUri = uncanonicalize(uri);
222001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        if (newUri != null) {
222101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen            return newUri;
222201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        }
222301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        return uri;
222401e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen    }
222501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
2226b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
2227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
2229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
223001e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
223101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        uri = safeUncanonicalize(uri);
223201e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen
2233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
2234baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        List<String> prependArgs = new ArrayList<String>();
2235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
223601a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
2237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
2238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
2239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
2240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
2241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
22430027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
22440027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                c.addRow(new String[] {mMediaScannerVolume});
22450027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                return c;
2246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
22490027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // Used temporarily (until we have unique media IDs) to get an identifier
22500027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // for the current sd card, so that the music app doesn't have to use the
22510027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // non-public getFatVolumeId method
22520027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        if (table == FS_ID) {
22530027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"fsid"});
22540027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            c.addRow(new Integer[] {mVolumeId});
22550027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            return c;
22560027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        }
22570027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
2258704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        if (table == VERSION) {
2259704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"version"});
226090c7da0610b7c3f4d9f4f3b2767e0bae5f3ab258Marco Nelissen            c.addRow(new Integer[] {getDatabaseVersion(getContext())});
2261704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen            return c;
2262704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        }
2263704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen
2264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
226510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
226610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
2268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
226910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
227010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getReadableDatabase();
22715fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) return null;
2272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
22734574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
2274c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String filter = uri.getQueryParameter("filter");
2275c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        String [] keywords = null;
2276c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        if (filter != null) {
2277c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            filter = Uri.decode(filter).trim();
2278c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            if (!TextUtils.isEmpty(filter)) {
2279c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                String [] searchWords = filter.split(" ");
2280c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                keywords = new String[searchWords.length];
2281c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; i < searchWords.length; i++) {
2282c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    String key = MediaStore.Audio.keyFor(searchWords[i]);
2283c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("\\", "\\\\");
2284c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("%", "\\%");
2285c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    key = key.replace("_", "\\_");
2286c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    keywords[i] = key;
2287c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2288c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen            }
2289c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen        }
2290db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        if (uri.getQueryParameter("distinct") != null) {
2291db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin            qb.setDistinct(true);
2292db8357b2b2888ce3778278e9cd9e698347ca6105Owen Lin        }
2293c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen
2294b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
2295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
2297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
2308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
2309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
2310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
2312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
2313baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2314baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2318b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2319b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
2320b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
2321b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2322b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2326d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2327ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.equalsIgnoreCase("is_music=1")
2328ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                          || selection.equalsIgnoreCase("is_podcast=1") )
2329c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2330c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2331ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting songs");
2332ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2333ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2334ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio");
2335c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2336c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2337c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2338c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2339c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2340c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2341baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                "||" + MediaStore.Audio.Media.TITLE_KEY + " LIKE ? ESCAPE '\\'");
2342baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2343c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2344ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
2349baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2350baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
2356baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_genres_map WHERE audio_id=?)");
2357baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2362baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2363baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
2369baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "audio_playlists_map WHERE audio_id=?)");
2370baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2375baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2376baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(5));
2377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
2385baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2386baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2389bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen            case AUDIO_GENRES_ALL_MEMBERS:
2390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
239178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                {
239278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // if simpleQuery is true, we can do a simpler query on just audio_genres_map
239378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // we can do this if we have no keywords and our projection includes just columns
239478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    // from audio_genres_map
239578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    boolean simpleQuery = (keywords == null && projectionIn != null
239678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            && (selection == null || selection.equalsIgnoreCase("genre_id=?")));
239778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (projectionIn != null) {
239878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; i < projectionIn.length; i++) {
239978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            String p = projectionIn[i];
240078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (p.equals("_id")) {
240178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // note, this is different from playlist below, because
240278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // "_id" used to (wrongly) be the audio id in this query, not
240378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // the row id of the entry in the map, and we preserve this
240478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                // behavior for backwards compatibility
240578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
240678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
240778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            if (simpleQuery && !(p.equals("audio_id") ||
240878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    p.equals("genre_id"))) {
240978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                simpleQuery = false;
241078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            }
241178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
241278b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
241378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    if (simpleQuery) {
241478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid");
2415bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2416baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere("genre_id=?");
2417baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2418bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
241978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    } else {
242078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        qb.setTables("audio_genres_map_noid, audio");
2421bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        qb.appendWhere("audio._id = audio_id");
2422bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        if (table == AUDIO_GENRES_ID_MEMBERS) {
2423baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            qb.appendWhere(" AND genre_id=?");
2424baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add(uri.getPathSegments().get(3));
2425bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen                        }
242678b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        for (int i = 0; keywords != null && i < keywords.length; i++) {
242778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(" AND ");
242878b2885edc406273d688536b0eadfea006b20662Marco Nelissen                            qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
242978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.ALBUM_KEY +
243078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                    "||" + MediaStore.Audio.Media.TITLE_KEY +
2431baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                    " LIKE ? ESCAPE '\\'");
2432baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            prependArgs.add("%" + keywords[i] + "%");
243378b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        }
243478b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    }
243578b2885edc406273d688536b0eadfea006b20662Marco Nelissen                }
2436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
2444baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2445baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2448b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2450e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // if simpleQuery is true, we can do a simpler query on just audio_playlists_map
2451e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // we can do this if we have no keywords and our projection includes just columns
2452e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                // from audio_playlists_map
24534382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                boolean simpleQuery = (keywords == null && projectionIn != null
24544382d5ecf11d3c70eed9ba7b09970ef254774b6dMike Lockwood                        && (selection == null || selection.equalsIgnoreCase("playlist_id=?")));
245597e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
245697e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
2457e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        String p = projectionIn[i];
2458e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (simpleQuery && !(p.equals("audio_id") ||
2459e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                p.equals("playlist_id") || p.equals("play_order"))) {
2460e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                            simpleQuery = false;
2461e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        }
2462e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        if (p.equals("_id")) {
246397e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
246497e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
2465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2467e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                if (simpleQuery) {
2468e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map");
2469baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("playlist_id=?");
2470baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2471e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                } else {
2472e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    qb.setTables("audio_playlists_map, audio");
2473baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere("audio._id = audio_id AND playlist_id=?");
2474baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(3));
2475e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2476e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(" AND ");
2477e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2478e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2479e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                                "||" + MediaStore.Audio.Media.TITLE_KEY +
2480baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2481baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2482e6300fefefc68417851efcc455cc01a4aaa60678Mike Lockwood                    }
2483c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2484b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                if (table == AUDIO_PLAYLISTS_ID_MEMBERS_ID) {
2485baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    qb.appendWhere(" AND audio_playlists_map._id=?");
2486baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add(uri.getPathSegments().get(5));
2487b5f293f3888b304e0b78c0039d7326c20e778b9fMarco Nelissen                }
2488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
2495baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2496baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2499b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
2500b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
2501b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
2502b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
2503b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
2504b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
2505b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
2506b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
2508d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2509ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2510c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2511c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2512ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting artists");
2513ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2514ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct artist_id)";
2515ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2516ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2517ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("artist_info");
2518c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2519c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2520c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2521c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2522c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2523baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2524baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2525c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2526ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
2530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
2531baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2532baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
2536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
2537acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
2538acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        " audio.album_id=album_art.album_id");
2539acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
2540baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        "artists_albums_map WHERE artist_id=?)");
2541baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(aid);
2542c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                for (int i = 0; keywords != null && i < keywords.length; i++) {
2543c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(" AND ");
2544c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2545c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            "||" + MediaStore.Audio.Media.ALBUM_KEY +
2546baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                            " LIKE ? ESCAPE '\\'");
2547baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                    prependArgs.add("%" + keywords[i] + "%");
2548c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                }
2549acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                groupBy = "audio.album_id";
2550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
2551acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
2552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
2553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
2554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
2557d9672f4d28ab9038333a0037fe30e386ac0ce610Marco Nelissen                if (projectionIn != null && projectionIn.length == 1 &&  selectionArgs == null
2558ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                        && (selection == null || selection.length() == 0)
2559c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && projectionIn[0].equalsIgnoreCase("count(*)")
2560c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        && keywords != null) {
2561ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    //Log.i("@@@@", "taking fast path for counting albums");
2562ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("audio_meta");
2563ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    projectionIn[0] = "count(distinct album_id)";
2564ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.appendWhere("is_music=1");
2565ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                } else {
2566ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                    qb.setTables("album_info");
2567c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    for (int i = 0; keywords != null && i < keywords.length; i++) {
2568c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        if (i > 0) {
2569c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                            qb.appendWhere(" AND ");
2570c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        }
2571c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                        qb.appendWhere(MediaStore.Audio.Media.ARTIST_KEY +
2572c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                                "||" + MediaStore.Audio.Media.ALBUM_KEY +
2573baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                                " LIKE ? ESCAPE '\\'");
2574baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        prependArgs.add("%" + keywords[i] + "%");
2575c38530b8c1935f629023df6e9a478c2c801bdddaMarco Nelissen                    }
2576ea8ed74e997619531921fb4138728b7d65c35089Marco Nelissen                }
2577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
2580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
2581baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2582baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
2586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
2587baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("album_id=?");
2588baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(3));
2589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2591a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
2592a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
2593a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
2594a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
2595a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
2596baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection,
2597baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                        combine(prependArgs, selectionArgs), sort, table, limit);
2598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
259916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
2600e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
2601baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                qb.appendWhere("_id=?");
2602baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                prependArgs.add(uri.getPathSegments().get(2));
2603b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                // fall through
260416dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
2605e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
260616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                qb.setTables("files");
2607b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                break;
2608b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
2609e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            case MTP_OBJECT_REFERENCES:
2610e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                int handle = Integer.parseInt(uri.getPathSegments().get(2));
261110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                return getObjectReferences(helper, db, handle);
2612e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
2613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
2614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
2615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2617baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection,
2618baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        //        combine(prependArgs, selectionArgs), groupBy, null, sort, limit));
2619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
2620baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen                combine(prependArgs, selectionArgs), groupBy, null, sort, limit);
2621b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
2623ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            String nonotify = uri.getQueryParameter("nonotify");
2624ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            if (nonotify == null || !nonotify.equals("1")) {
2625ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen                c.setNotificationUri(getContext().getContentResolver(), uri);
2626ff55c35921e1215d39adf620db7d71f9839103c1Marco Nelissen            }
2627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2628b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2632baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    private String[] combine(List<String> prepend, String[] userArgs) {
2633baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int presize = prepend.size();
2634baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        if (presize == 0) {
2635baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            return userArgs;
2636baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2637baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2638baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        int usersize = (userArgs != null) ? userArgs.length : 0;
2639baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        String [] combined = new String[presize + usersize];
2640baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < presize; i++) {
2641baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[i] = prepend.get(i);
2642baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2643baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        for (int i = 0; i < usersize; i++) {
2644baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen            combined[presize + i] = userArgs[i];
2645baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        }
2646baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen        return combined;
2647baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen    }
2648baffe34089b74c09d549c963da24ffb80f6682f4Marco Nelissen
2649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
2650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
26514574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
26524574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
2653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
265418c787fb045725bf10bf630ac0917a48def9ace5Marco Nelissen        String mSearchString = uri.getPath().endsWith("/") ? "" : uri.getLastPathSegment();
2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
2656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
2658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
2659a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
2660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
2661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
2662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
2663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
26643001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            String key = MediaStore.Audio.keyFor(searchWords[i]);
26653001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("\\", "\\\\");
26663001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("%", "\\%");
26673001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen            key = key.replace("_", "\\_");
2668a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
2669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
26703001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                        searchWords[i].equals("the")) ? "%" : "%" + key + "%";
2671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2673a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
2674a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
2675a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
26763001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where = "match LIKE ? ESCAPE '\\'";
2677a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
26783001ef332046c69cbb70289be29442fcc0ad5f6fMarco Nelissen                where += " AND match LIKE ? ESCAPE '\\'";
2679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2682a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
2683a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
2684a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
2685a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
2686a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
2687a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
2688a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
2689a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
2690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
26914574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
2692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
2696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
2698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
2699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
2700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
2702c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case FILES_ID:
270326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                Cursor c = null;
270426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                try {
270526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    c = query(url, MIME_TYPE_PROJECTION, null, null, null);
270626f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null && c.getCount() == 1) {
270726f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.moveToFirst();
270826f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        String mimeType = c.getString(1);
270926f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.deactivate();
271026f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        return mimeType;
271126f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
271226f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                } finally {
271326f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    if (c != null) {
271426f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                        c.close();
271526f297abf92299b21ad5ddc8f722bd83805e1bc7Ray Chen                    }
2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
2718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
2720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
2721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
2722804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen            case AUDIO_ALBUMART_ID:
2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
2727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
2728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
2729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
2730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
2732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
2733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
2734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
2735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
2736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
2737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
2738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
2739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
2740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
2741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
2742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
2743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
2745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
2746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2747804f5fe841d4a96f9335ea60d60853352f726227Marco Nelissen        throw new IllegalStateException("Unknown URL : " + url);
2748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
275250c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen     * present a new filename is generated. The file itself is not created.
2753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
2755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
2756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
2758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
2759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
2760801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        String file = initialValues.getAsString(MediaStore.MediaColumns.DATA);
2761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
2762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
2763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
2764801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            values.put(MediaStore.MediaColumns.DATA, file);
2765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
2767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
276992013781ab1573eee3d5d75682f320fe3a6076f2Marco Nelissen        // we used to create the file here, but now defer this until openFile() is called
2770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
2771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2773d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectAdded(long objectHandle) {
277434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
277534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
277634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
277734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectAdded((int)objectHandle);
277834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
277934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectAdded", e);
278034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
278134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2782d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2783d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2784d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2785d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2786d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    private void sendObjectRemoved(long objectHandle) {
278734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood        synchronized (mMtpServiceConnection) {
278834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            if (mMtpService != null) {
278934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                try {
279034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService.sendObjectRemoved((int)objectHandle);
279134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } catch (RemoteException e) {
279234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    Log.e(TAG, "RemoteException in sendObjectRemoved", e);
279334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
279434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
2795d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood            }
2796d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        }
2797d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood    }
2798d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood
2799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
2801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
2803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
2804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
280510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
280610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
2807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
281010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
28115fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        if (db == null) {
28125fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            throw new IllegalStateException("Couldn't open database for " + uri);
28135fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang        }
2814ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2815ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
2816ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            return playlistBulkInsert(db, uri, values);
2817e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } else if (match == MTP_OBJECT_REFERENCES) {
2818e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            int handle = Integer.parseInt(uri.getPathSegments().get(2));
281910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            return setObjectReferences(helper, db, handle, values);
2820ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2821ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
28222dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
28242dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
2825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
2826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
2828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
2829801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                if (values[i] != null) {
28302dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                    insertInternal(uri, match, values[i], notifyRowIds);
2831801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                }
2832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
2834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
2835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
2837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
28382dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
28392dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        // Notify MTP (outside of successful transaction)
28402dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        notifyMtp(notifyRowIds);
28412dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
2843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
2844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2847801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood    public Uri insert(Uri uri, ContentValues initialValues) {
28485d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        int match = URI_MATCHER.match(uri);
28492dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
28502dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        ArrayList<Long> notifyRowIds = new ArrayList<Long>();
28512dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
28522dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        notifyMtp(notifyRowIds);
28532dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
28545d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // do not signal notification for MTP objects.
28555d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        // we will signal instead after file transfer is successful.
2856e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        if (newUri != null && match != MTP_OBJECTS) {
2857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
2858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
2860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
28622dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    private void notifyMtp(ArrayList<Long> rowIds) {
28632dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        int size = rowIds.size();
28642dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        for (int i = 0; i < size; i++) {
28652dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke            sendObjectAdded(rowIds.get(i).longValue());
28662dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke        }
28672dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    }
28682dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke
2869ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) {
2870ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        DatabaseUtils.InsertHelper helper =
2871ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            new DatabaseUtils.InsertHelper(db, "audio_playlists_map");
28728b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
28738b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID);
28748b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
28758b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        long playlistId = Long.parseLong(uri.getPathSegments().get(3));
2876ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
2877ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        db.beginTransaction();
2878ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        int numInserted = 0;
2879ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        try {
2880ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            int len = values.length;
2881ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            for (int i = 0; i < len; i++) {
28828b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.prepareForInsert();
28838b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // getting the raw Object and converting it long ourselves saves
28848b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // an allocation (the alternative is ContentValues.getAsLong, which
28858b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // returns a Long object)
28868b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                long audioid = ((Number) values[i].get(
28878b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue();
28888b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(audioidcolidx, audioid);
28898b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playlistididx, playlistId);
28908b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // convert to int ourselves to save an allocation.
28918b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                int playorder = ((Number) values[i].get(
28928b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue();
28938b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playorderidx, playorder);
28948b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.execute();
2895ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            }
2896ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            numInserted = len;
2897ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.setTransactionSuccessful();
2898ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        } finally {
2899ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.endTransaction();
2900ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            helper.close();
2901ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
2902ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        getContext().getContentResolver().notifyChange(uri, null);
2903ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        return numInserted;
2904ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    }
2905ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
290610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertDirectory(DatabaseHelper helper, SQLiteDatabase db, String path) {
290710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (LOCAL_LOGV) Log.v(TAG, "inserting directory " + path);
2908ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        ContentValues values = new ContentValues();
2909ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ASSOCIATION);
2910ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        values.put(FileColumns.DATA, path);
291110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        values.put(FileColumns.PARENT, getParent(helper, db, path));
29129be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        values.put(FileColumns.STORAGE_ID, getStorageId(path));
2913f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        File file = new File(path);
2914f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        if (file.exists()) {
2915f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
2916f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood        }
291710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumInserts++;
2918ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        long rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
2919ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        sendObjectAdded(rowId);
2920ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood        return rowId;
2921ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood    }
2922ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
292310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getParent(DatabaseHelper helper, SQLiteDatabase db, String path) {
2924b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        int lastSlash = path.lastIndexOf('/');
2925b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        if (lastSlash > 0) {
2926b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String parentPath = path.substring(0, lastSlash);
29279be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            for (int i = 0; i < mExternalStoragePaths.length; i++) {
29289be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (parentPath.equals(mExternalStoragePaths[i])) {
29299be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return 0;
29309be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
2931b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
29327f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Long cid = mDirectoryCache.get(parentPath);
29337f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            if (cid != null) {
29347f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                if (LOCAL_LOGV) Log.v(TAG, "Returning cached entry for " + parentPath);
29357f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return cid;
29367f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            }
29377f36494e085c26c69cd5925e54028822025eff29Marco Nelissen
2938e870bcda353ccda42bf677f643036c130e10ce2bMarco Nelissen            String selection = MediaStore.MediaColumns.DATA + "=?";
2939b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            String [] selargs = { parentPath };
294010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
29417f36494e085c26c69cd5925e54028822025eff29Marco Nelissen            Cursor c = db.query("files", sIdOnlyColumn, selection, selargs, null, null, null);
2942b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            try {
29437f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                long id;
2944b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c == null || c.getCount() == 0) {
2945b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    // parent isn't in the database - so add it
294610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    id = insertDirectory(helper, db, parentPath);
29477f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Inserted " + parentPath);
2948b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                } else {
29495461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    if (c.getCount() > 1) {
29505461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        Log.e(TAG, "more than one match for " + parentPath);
29515461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    }
2952b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                    c.moveToFirst();
29537f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    id = c.getLong(0);
29547f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                    if (LOCAL_LOGV) Log.v(TAG, "Queried " + parentPath);
2955b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                }
29567f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.put(parentPath, id);
29577f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                return id;
2958b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            } finally {
2959b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood                if (c != null) c.close();
2960b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            }
2961b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        } else {
2962b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood            return 0;
2963b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        }
2964b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood    }
2965b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
29669be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    private int getStorageId(String path) {
29679be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        for (int i = 0; i < mExternalStoragePaths.length; i++) {
29689be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            String test = mExternalStoragePaths[i];
29699be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (path.startsWith(test)) {
29709be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int length = test.length();
29719be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                if (path.length() == length || path.charAt(length) == '/') {
29729be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    return MtpStorage.getStorageId(i);
29739be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                }
29749be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
29759be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        }
29769be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        // default to primary storage
29779be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood        return MtpStorage.getStorageId(0);
29789be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood    }
29799be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood
298010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, int mediaType,
29812dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                            boolean notify, ArrayList<Long> notifyRowIds) {
298210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
2983afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        ContentValues values = null;
2984afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2985afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        switch (mediaType) {
2986afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_IMAGE: {
298710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".jpg", "DCIM/Camera");
2988afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2989afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
2990afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String data = values.getAsString(MediaColumns.DATA);
2991afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
2992afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    computeDisplayName(data, values);
2993afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
2994afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
2995afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
2996afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
2997afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
2998afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_AUDIO: {
2999afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // SQLite Views are read-only, so we need to deconstruct this
3000afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // insert and do inserts into the underlying tables.
3001afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // If doing this here turns out to be a performance bottleneck,
3002afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // consider moving this to native code and using triggers on
3003afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the view.
3004afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values = new ContentValues(initialValues);
3005afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
30062658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
30072658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
30082658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                values.remove(MediaStore.Audio.Media.COMPILATION);
30092658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
3010afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Insert the artist into the artist table and remove it from
3011afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // the input values
3012afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                Object so = values.get("artist");
3013afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String s = (so == null ? "" : so.toString());
3014afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("artist");
3015afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long artistRowId;
301610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> artistCache = helper.mArtistCache;
3017801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String path = values.getAsString(MediaStore.MediaColumns.DATA);
3018afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(artistCache) {
3019afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = artistCache.get(s);
3020afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
302110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        artistRowId = getKeyIdForName(helper, db,
302210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "artists", "artist_key", "artist",
3023afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, s, path, 0, null, artistCache, uri);
3024afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
3025afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        artistRowId = temp.longValue();
3026afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
3027afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3028afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                String artist = s;
3029afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3030afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // Do the same for the album field
3031afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.get("album");
3032afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
3033afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("album");
3034afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long albumRowId;
303510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                HashMap<String, Long> albumCache = helper.mAlbumCache;
3036afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                synchronized(albumCache) {
30372658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    int albumhash = 0;
30382658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    if (albumartist != null) {
30392658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = albumartist.hashCode();
30402658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else if (compilation != null && compilation.equals("1")) {
30412658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        // nothing to do, hash already set
30422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    } else {
30432658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
30442658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                    }
3045afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    String cacheName = s + albumhash;
3046afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    Long temp = albumCache.get(cacheName);
3047afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    if (temp == null) {
304810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        albumRowId = getKeyIdForName(helper, db,
304910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                "albums", "album_key", "album",
3050afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                s, cacheName, path, albumhash, artist, albumCache, uri);
3051afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    } else {
3052afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        albumRowId = temp;
3053afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    }
3054afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
30555d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
3056afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("artist_id", Integer.toString((int)artistRowId));
3057afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("album_id", Integer.toString((int)albumRowId));
3058afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                so = values.getAsString("title");
3059afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                s = (so == null ? "" : so.toString());
3060afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title_key", MediaStore.Audio.keyFor(s));
3061afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // do a final trim of the title, in case it started with the special
3062afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                // "sort first" character (ascii \001)
3063afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.remove("title");
3064afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                values.put("title", s.trim());
3065b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3066801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                computeDisplayName(values.getAsString(MediaStore.MediaColumns.DATA), values);
3067afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
3068afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
3069afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3070afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FileColumns.MEDIA_TYPE_VIDEO: {
307110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                values = ensureFile(helper.mInternal, initialValues, ".3gp", "video");
3072801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String data = values.getAsString(MediaStore.MediaColumns.DATA);
3073afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeDisplayName(data, values);
3074afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                computeTakenTime(values);
3075afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                break;
3076afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
3077afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
3078afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3079afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (values == null) {
3080afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(initialValues);
3081afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
3082fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        // compute bucket_id and bucket_display_name for all files
3083afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String path = values.getAsString(MediaStore.MediaColumns.DATA);
3084fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        if (path != null) {
3085fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood            computeBucketValues(path, values);
3086fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        }
3087fb598dd5ac235f6282aac23b7b9c214f2fd62a7aMike Lockwood        values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
3088afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3089afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        long rowId = 0;
3090afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer i = values.getAsInteger(
3091afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
3092afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (i != null) {
3093afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = i.intValue();
3094afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values = new ContentValues(values);
3095afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            values.remove(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID);
3096afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        }
3097afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3098afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String title = values.getAsString(MediaStore.MediaColumns.TITLE);
30993572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen        if (title == null && path != null) {
3100c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            title = MediaFile.getFileTitle(path);
3101c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3102c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        values.put(FileColumns.TITLE, title);
3103c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
3104afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        String mimeType = values.getAsString(MediaStore.MediaColumns.MIME_TYPE);
3105afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        Integer formatObject = values.getAsInteger(FileColumns.FORMAT);
3106c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        int format = (formatObject == null ? 0 : formatObject.intValue());
3107c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format == 0) {
310863b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang            if (TextUtils.isEmpty(path)) {
3109c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                // special case device created playlists
3110afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
3111c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(FileColumns.FORMAT, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST);
3112c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    // create a file path for the benefit of MTP
31139be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                    path = mExternalStoragePaths[0]
3114afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            + "/Playlists/" + values.getAsString(Audio.Playlists.NAME);
3115c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                    values.put(MediaStore.MediaColumns.DATA, path);
311610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    values.put(FileColumns.PARENT, getParent(helper, db, path));
3117c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                } else {
311863b4ed85f500e23297ad5eb530318e06b9ab2289Dongwon Kang                    Log.e(TAG, "path is empty in insertFile()");
3119c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                }
3120c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            } else {
3121c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                format = MediaFile.getFormatCode(path, mimeType);
3122c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3123c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3124c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (format != 0) {
3125c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.FORMAT, format);
3126c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            if (mimeType == null) {
3127c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood                mimeType = MediaFile.getMimeTypeForFormatCode(format);
3128c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3129c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3130c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
3131801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood        if (mimeType == null && path != null) {
3132c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            mimeType = MediaFile.getMimeTypeForFile(path);
3133c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3134c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        if (mimeType != null) {
3135c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            values.put(FileColumns.MIME_TYPE, mimeType);
3136afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3137f1f6a9e343033de33fc748f659b9221f8d5b06e1Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_NONE && !MediaScanner.isNoMediaPath(path)) {
3138afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int fileType = MediaFile.getFileTypeForMimeType(mimeType);
3139afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (MediaFile.isAudioFileType(fileType)) {
3140afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_AUDIO;
3141afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isVideoFileType(fileType)) {
3142afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_VIDEO;
3143afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isImageFileType(fileType)) {
3144afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_IMAGE;
3145afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                } else if (MediaFile.isPlayListFileType(fileType)) {
3146afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
3147afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3148afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
3149c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood        }
3150afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        values.put(FileColumns.MEDIA_TYPE, mediaType);
3151c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood
3152afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        if (rowId == 0) {
3153afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
31543572ac7fc911cda5e4ac60af9455820a7ddf6593Marco Nelissen                String name = values.getAsString(Audio.Playlists.NAME);
3155282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                if (name == null && path == null) {
3156282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                    // MediaScanner will compute the name from the path if we have one
3157afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
3158282dc90a0e201d992cdd0d0176028da6a565f9d5Mike Lockwood                            "no name was provided when inserting abstract playlist");
3159afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3160afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            } else {
3161a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu                if (path == null) {
3162afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // path might be null for playlists created on the device
3163afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    // or transfered via MTP
3164afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    throw new IllegalArgumentException(
3165afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                            "no path was provided when inserting new file");
3166afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                }
3167e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3168b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3169f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            // make sure modification date and size are set
3170f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood            if (path != null) {
3171f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                File file = new File(path);
3172f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                if (file.exists()) {
3173f2921d9af0a43c4be56a88990b55efe86fc7bb29Mike Lockwood                    values.put(FileColumns.DATE_MODIFIED, file.lastModified() / 1000);
317434d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                    if (!values.containsKey(FileColumns.SIZE)) {
317534d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                        values.put(FileColumns.SIZE, file.length());
317634d6cfab3faf234d36ac3da63dcc50ef3d5fc1a5Marco Nelissen                    }
317720368a558593c685623bfff6df1f4545d53f8291Jia Su                    // make sure date taken time is set
317820368a558593c685623bfff6df1f4545d53f8291Jia Su                    if (mediaType == FileColumns.MEDIA_TYPE_IMAGE
317920368a558593c685623bfff6df1f4545d53f8291Jia Su                            || mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
318020368a558593c685623bfff6df1f4545d53f8291Jia Su                        computeTakenTime(values);
318120368a558593c685623bfff6df1f4545d53f8291Jia Su                    }
3182e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
31835d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
3184b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
3185afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            Long parent = values.getAsLong(FileColumns.PARENT);
31865d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            if (parent == null) {
3187e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (path != null) {
318810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    long parentId = getParent(helper, db, path);
318916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                    values.put(FileColumns.PARENT, parentId);
3190e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
31919be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            }
31929be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            Integer storage = values.getAsInteger(FileColumns.STORAGE_ID);
31939be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            if (storage == null) {
31949be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                int storageId = getStorageId(path);
31959be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                values.put(FileColumns.STORAGE_ID, storageId);
31965d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood            }
3197b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
319810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumInserts++;
3199afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            rowId = db.insert("files", FileColumns.DATE_MODIFIED, values);
3200afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            if (LOCAL_LOGV) Log.v(TAG, "insertFile: values=" + values + " returned: " + rowId);
3201afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
320210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (rowId != -1 && notify) {
32032dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                notifyRowIds.add(rowId);
3204afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            }
32055d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        } else {
320610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates++;
320716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            db.update("files", values, FileColumns._ID + "=?",
3208afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    new String[] { Long.toString(rowId) });
32095d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood        }
3210d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen        if (format == MtpConstants.FORMAT_ASSOCIATION) {
3211d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen            mDirectoryCache.put(path, rowId);
3212d4e1312b4927b2bdd1f47e3d989f7293635c1125Marco Nelissen        }
3213afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood
3214afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        return rowId;
32151717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    }
32161717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
321710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private Cursor getObjectReferences(DatabaseHelper helper, SQLiteDatabase db, int handle) {
321810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
3219a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
3220e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
3221e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
3222e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
3223e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
3224afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                long playlistId = c.getLong(0);
3225afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
3226afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
3227e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
3228e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return null;
3229e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
323010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumQueries++;
3231e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                return db.rawQuery(OBJECT_REFERENCES_QUERY,
3232afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        new String[] { Long.toString(playlistId) } );
3233e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3234e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
3235e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
3236e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
3237e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3238e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3239e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return null;
3240e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
3241e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
324210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int setObjectReferences(DatabaseHelper helper, SQLiteDatabase db,
324310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            int handle, ContentValues values[]) {
3244e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // first look up the media table and media ID for the object
3245e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        long playlistId = 0;
324610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
3247a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen        Cursor c = db.query("files", sMediaTableColumns, "_id=?",
3248e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] {  Integer.toString(handle) },
3249e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                null, null, null);
3250e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        try {
3251e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null && c.moveToNext()) {
3252afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                int mediaType = c.getInt(1);
3253afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (mediaType != FileColumns.MEDIA_TYPE_PLAYLIST) {
3254e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    // we only support object references for playlist objects
3255e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    return 0;
3256e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3257afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                playlistId = c.getLong(0);
3258e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3259e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        } finally {
3260e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (c != null) {
3261e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                c.close();
3262e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3263e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3264e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (playlistId == 0) {
3265e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            return 0;
3266e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3267e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3268e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // next delete any existing entries
326910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumDeletes++;
327010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        db.delete("audio_playlists_map", "playlist_id=?",
3271e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                new String[] { Long.toString(playlistId) });
3272e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3273e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        // finally add the new entries
3274e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int count = values.length;
3275e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        int added = 0;
3276e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        ContentValues[] valuesList = new ContentValues[count];
3277e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        for (int i = 0; i < count; i++) {
3278e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // convert object ID to audio ID
3279e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long audioId = 0;
3280e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            long objectId = values[i].getAsLong(MediaStore.MediaColumns._ID);
328110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumQueries++;
3282a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen            c = db.query("files", sMediaTableColumns, "_id=?",
3283e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    new String[] {  Long.toString(objectId) },
3284e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    null, null, null);
3285e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            try {
3286e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null && c.moveToNext()) {
3287afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    int mediaType = c.getInt(1);
328850d8650456d93e2107b9163e119c2eb9de73f804Mike Lockwood                    if (mediaType != FileColumns.MEDIA_TYPE_AUDIO) {
3289e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        // we only allow audio files in playlists, so skip
3290e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                        continue;
3291e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    }
3292afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                    audioId = c.getLong(0);
3293e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3294e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            } finally {
3295e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                if (c != null) {
3296e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                    c.close();
3297e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                }
3298e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3299e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            if (audioId != 0) {
3300e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                ContentValues v = new ContentValues();
3301e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistId);
3302e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                v.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audioId);
3303a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                v.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, added);
3304a08bc533a5f7bf9aea49b25a549b4f5b3c14f47dMike Lockwood                valuesList[added++] = v;
3305e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            }
3306e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3307e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        if (added < count) {
3308e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // we weren't able to find everything on the list, so lets resize the array
3309e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            // and pass what we have.
3310e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            ContentValues[] newValues = new ContentValues[added];
3311e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            System.arraycopy(valuesList, 0, newValues, 0, added);
3312e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood            valuesList = newValues;
3313e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        }
3314e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood        return playlistBulkInsert(db,
3315e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                Audio.Playlists.Members.getContentUri(EXTERNAL_VOLUME, playlistId),
3316e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood                valuesList);
3317e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    }
3318e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
3319b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private static final String[] GENRE_LOOKUP_PROJECTION = new String[] {
3320b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres._ID, // 0
3321b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            Audio.Genres.NAME, // 1
3322b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    };
3323b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3324b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    private void updateGenre(long rowId, String genre) {
3325b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri uri = null;
3326b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Cursor cursor = null;
3327b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        Uri genresUri = MediaStore.Audio.Genres.getContentUri("external");
3328b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        try {
3329b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // see if the genre already exists
3330b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            cursor = query(genresUri, GENRE_LOOKUP_PROJECTION, MediaStore.Audio.Genres.NAME + "=?",
3331b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            new String[] { genre }, null);
3332b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor == null || cursor.getCount() == 0) {
3333b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre does not exist, so create the genre in the genre table
3334b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                ContentValues values = new ContentValues();
3335b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                values.put(MediaStore.Audio.Genres.NAME, genre);
3336b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = insert(genresUri, values);
3337b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            } else {
3338b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                // genre already exists, so compute its Uri
3339b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.moveToNext();
3340b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = ContentUris.withAppendedId(genresUri, cursor.getLong(0));
3341b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3342b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (uri != null) {
3343b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                uri = Uri.withAppendedPath(uri, MediaStore.Audio.Genres.Members.CONTENT_DIRECTORY);
3344b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3345b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        } finally {
3346b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // release the cursor if it exists
3347b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            if (cursor != null) {
3348b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                cursor.close();
3349b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            }
3350b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3351b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
3352b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (uri != null) {
3353b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            // add entry to audio_genre_map
3354b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            ContentValues values = new ContentValues();
3355b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            values.put(MediaStore.Audio.Genres.Members.AUDIO_ID, Long.valueOf(rowId));
3356b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            insert(uri, values);
3357b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3358b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood    }
3359b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
33602dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke    private Uri insertInternal(Uri uri, int match, ContentValues initialValues,
33612dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                               ArrayList<Long> notifyRowIds) {
3362b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        final String volumeName = getVolumeName(uri);
3363b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
3364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
3365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3366d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood        if (LOCAL_LOGV) Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
3367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3369bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
337010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
337110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
337210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
337310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
337410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
337510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStartTime = SystemClock.currentTimeMicro();
337610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
3378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3380b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
338138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String path = null;
3382b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
3383b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
3384b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
338538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            path = initialValues.getAsString(MediaStore.MediaColumns.DATA);
3386b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
3387b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
338838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
339010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
339110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null && match != VOLUMES && match != MTP_CONNECTED) {
3392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
3393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
3394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
339510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
3396819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        SQLiteDatabase db = ((match == VOLUMES || match == MTP_CONNECTED) ? null
339710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                : helper.getWritableDatabase());
3398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
3400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
34012dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
34022dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_IMAGE, true, notifyRowIds);
3403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3404b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    MediaDocumentsProvider.onMediaStoreInsert(
3405b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContext(), volumeName, FileColumns.MEDIA_TYPE_IMAGE, rowId);
3406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
3407b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Images.Media.getContentUri(volumeName), rowId);
3408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3412b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
3413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
341410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3415b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
341610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
3418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
3420b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContentUri(volumeName), rowId);
3421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3425b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
3426b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
342710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                ContentValues values = ensureFile(helper.mInternal, initialValues, ".jpg",
3428b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
342910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3430b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
3431b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
3432b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
3433b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContentUri(volumeName), rowId);
3434b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
3435b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3436b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
3437b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
34392dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
34402dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_AUDIO, true, notifyRowIds);
3441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3442b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    MediaDocumentsProvider.onMediaStoreInsert(
3443b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContext(), volumeName, FileColumns.MEDIA_TYPE_AUDIO, rowId);
3444b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = ContentUris.withAppendedId(
3445b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Audio.Media.getContentUri(volumeName), rowId);
3446b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    if (genre != null) {
3447b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        updateGenre(rowId, genre);
3448b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                    }
3449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
3454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3455bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
345710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3458ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_genres_map", "genre_id", values);
3459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
3466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
3467bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
346910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
3471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
3472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
347910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3480bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                rowId = db.insert("audio_genres", "audio_id", initialValues);
3481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3482b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = ContentUris.withAppendedId(
3483b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Audio.Genres.getContentUri(volumeName), rowId);
3484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
3489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
3490bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
349210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
3494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
3501bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
35032dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, values,
35042dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_PLAYLIST, true, notifyRowIds);
3505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3506b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = ContentUris.withAppendedId(
3507b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Audio.Playlists.getContentUri(volumeName), rowId);
3508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
3514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
3515bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = new ContentValues(initialValues);
3516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
351710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3518ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_playlists_map", "playlist_id", values);
3519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
35262dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                rowId = insertFile(helper, uri, initialValues,
35272dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_VIDEO, true, notifyRowIds);
3528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3529b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    MediaDocumentsProvider.onMediaStoreInsert(
3530b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            getContext(), volumeName, FileColumns.MEDIA_TYPE_VIDEO, rowId);
3531b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = ContentUris.withAppendedId(
3532b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            Video.Media.getContentUri(volumeName), rowId);
3533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3537c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            case AUDIO_ALBUMART: {
353810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                if (helper.mInternal) {
3539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
3540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3541bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                ContentValues values = null;
3542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
3543bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
3544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
3545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
3546bc442ef681710cca3d8eb1a57d6e81471c9987c0Mike Lockwood                    values = initialValues;
3547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
354810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
3549801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
3550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
3551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
3552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
3553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3554c198bd976a754d94863d1b50fe392376ded122a0Mike Lockwood            }
3555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
35575619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            {
35585619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                String name = initialValues.getAsString("name");
35595619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                Uri attachedVolume = attachVolume(name);
35605619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                if (mMediaScannerVolume != null && mMediaScannerVolume.equals(name)) {
35615619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    DatabaseHelper dbhelper = getDatabaseForUri(attachedVolume);
35625619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    if (dbhelper == null) {
35635619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                        Log.e(TAG, "no database for attached volume " + attachedVolume);
35645619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    } else {
35655619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                        dbhelper.mScanStartTime = SystemClock.currentTimeMicro();
35665619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                    }
35675619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                }
35685619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen                return attachedVolume;
35695619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            }
3570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3571819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            case MTP_CONNECTED:
357234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                synchronized (mMtpServiceConnection) {
357334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    if (mMtpService == null) {
357434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        Context context = getContext();
357534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        // MTP is connected, so grab a connection to MtpService
357634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                        context.bindService(new Intent(context, MtpService.class),
357734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                                mMtpServiceConnection, Context.BIND_AUTO_CREATE);
357834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    }
357934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
3580819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                break;
3581819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
3582afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood            case FILES:
358310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
35842dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_NONE, true, notifyRowIds);
3585fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                if (rowId > 0) {
3586b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = Files.getContentUri(volumeName, rowId);
3587fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                }
3588fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood                break;
3589fc824ed365b03ae272a3241b202367b1bdec18bcMike Lockwood
3590e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
35912dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                // We don't send a notification if the insert originated from MTP
359210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                rowId = insertFile(helper, uri, initialValues,
35932dc83e226f1016c117e9dc377aa6bcbc7328e10eDave Burke                        FileColumns.MEDIA_TYPE_NONE, false, notifyRowIds);
3594afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                if (rowId > 0) {
3595b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                    newUri = Files.getMtpObjectsUri(volumeName, rowId);
35965d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                }
35975d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood                break;
35985d7e71a87b085817aef6e77cd4c2a6dc7d983f9fMike Lockwood
3599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
3601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
360338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path != null && path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
360438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            // need to set the media_type of all the files below this folder to 0
360538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processNewNoMediaPath(helper, db, path);
360638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
3607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
3608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
361038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
361138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Sets the media type of all files below the newly added .nomedia file or
361238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * hidden folder to 0, so the entries no longer appear in e.g. the audio and
361338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * images views.
361438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     *
361538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * @param path The path to the new .nomedia file or hidden directory
361638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
36175461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    private void processNewNoMediaPath(final DatabaseHelper helper, final SQLiteDatabase db,
36185461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            final String path) {
36195461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        final File nomedia = new File(path);
362038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (nomedia.exists()) {
36215461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            hidePath(helper, db, path);
36225461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        } else {
36235461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            // File doesn't exist. Try again in a little while.
36245461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            // XXX there's probably a better way of doing this
36255461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            new Thread(new Runnable() {
36265461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                @Override
36275461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                public void run() {
36285461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    SystemClock.sleep(2000);
36295461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    if (nomedia.exists()) {
36305461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        hidePath(helper, db, path);
36315461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    } else {
36325461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                        Log.w(TAG, "does not exist: " + path, new Exception());
36335461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    }
36345461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                }}).start();
363538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
363638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
363738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
36385461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    private void hidePath(DatabaseHelper helper, SQLiteDatabase db, String path) {
36395461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        File nomedia = new File(path);
36405461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        String hiddenroot = nomedia.isDirectory() ? path : nomedia.getParent();
36415461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        ContentValues mediatype = new ContentValues();
36425461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        mediatype.put("media_type", 0);
36435461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        int numrows = db.update("files", mediatype,
3644a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                "_data >= ? AND _data < ?",
3645a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang                new String[] { hiddenroot  + "/", hiddenroot + "0"});
36465461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        helper.mNumUpdates += numrows;
36475461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        ContentResolver res = getContext().getContentResolver();
36485461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen        res.notifyChange(Uri.parse("content://media/"), null);
36495461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen    }
36505461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen
365138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    /*
365238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * Rescan files for missing metadata and set their type accordingly.
365338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * There is code for detecting the removal of a nomedia file or renaming of
365438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * a directory from hidden to non-hidden in the MediaScanner and MtpDatabase,
365538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     * both of which call here.
365638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen     */
365738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private void processRemovedNoMediaPath(final String path) {
365838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        final DatabaseHelper helper;
365938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (path.startsWith(mExternalStoragePaths[0])) {
366038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
366138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        } else {
366238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            helper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
366338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
366438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
366538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        new ScannerClient(getContext(), db, path);
366638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
366738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
366838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    private static final class ScannerClient implements MediaScannerConnectionClient {
366938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        String mPath = null;
367038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        MediaScannerConnection mScannerConnection;
367138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        SQLiteDatabase mDb;
367238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
367338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public ScannerClient(Context context, SQLiteDatabase db, String path) {
367438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mDb = db;
367538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mPath = path;
367638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection = new MediaScannerConnection(context, this);
367738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.connect();
367838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
367938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
368038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
368138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onMediaScannerConnected() {
36825461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen            Cursor c = mDb.query("files", openFileColumns,
3683a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                    "_data >= ? AND _data < ?",
3684a68a3f24c605b319d575abe9bef66a6f5ad99b80yi.jang                    new String[] { mPath + "/", mPath + "0"},
36855461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                    null, null, null);
368638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            while (c.moveToNext()) {
368738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                String d = c.getString(0);
368838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(d);
368938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (f.isFile()) {
369038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    mScannerConnection.scanFile(d, null);
369138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                }
369238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            }
369338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            mScannerConnection.disconnect();
369438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            c.close();
369538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
369638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
369738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        @Override
369838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        public void onScanCompleted(String path, Uri uri) {
369938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
370038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
370138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
3702cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    @Override
3703cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
3704cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                throws OperationApplicationException {
3705cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3706cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // The operations array provides no overall information about the URI(s) being operated
3707cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // on, so begin a transaction for ALL of the databases.
3708cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
3709cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
3710cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase idb = ihelper.getWritableDatabase();
3711cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        idb.beginTransaction();
3712cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase edb = null;
3713cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (ehelper != null) {
3714cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb = ehelper.getWritableDatabase();
3715cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb.beginTransaction();
3716cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3717cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        try {
3718cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentProviderResult[] result = super.applyBatch(operations);
3719cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.setTransactionSuccessful();
3720cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3721cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.setTransactionSuccessful();
3722cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3723cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // Rather than sending targeted change notifications for every Uri
3724cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // affected by the batch operation, just invalidate the entire internal
3725cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // and external name space.
3726cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentResolver res = getContext().getContentResolver();
3727cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
3728cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            return result;
3729cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        } finally {
3730cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.endTransaction();
3731cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
3732cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.endTransaction();
3733cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
3734cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
3735cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    }
3736cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
3737cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
37389299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) {
3739b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
3740e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            MediaThumbRequest req = null;
3741e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            try {
3742e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                req = new MediaThumbRequest(
37439299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        getContext().getContentResolver(), path, uri, priority, magic);
3744e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                mMediaThumbQueue.add(req);
3745e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                // Trigger the handler.
3746e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
3747e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                msg.sendToTarget();
3748e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            } catch (Throwable t) {
3749e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Log.w(TAG, t);
3750e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            }
3751b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
3752b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
3753b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
3754b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
3756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
3757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
3758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
3759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
3761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
3762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
3763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
3764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
37659be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood            return mExternalStoragePaths[0] + "/" + directoryName + "/" + name + preferredExtension;
3766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private boolean ensureFileExists(String path) {
3770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
3771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
3772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
3773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
3775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
3776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
3777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
3778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
3779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
3780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
3781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
378217ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood            file.getParentFile().mkdirs();
3783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
378417ad80b32f839ccddac3911799ff732d1ca3a006Mike Lockwood                return file.createNewFile();
3785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
3786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
3787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
3789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
3793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
3794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
3795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
3798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
3799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
3801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
3802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
3803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
38049f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
3805afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3806afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_IMAGE;
38079f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
38089f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
3809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
3810afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
3812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3814b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
3815b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3816b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
3817b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
3818b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3819b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
3820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
3821afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3822afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_AUDIO;
3823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
3826afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
3831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
3836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
3839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
3840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
3842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
3844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
3847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
3848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
3849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
3850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
3853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
3857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
3862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
3863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
3864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
3867afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3868afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_PLAYLIST;
3869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
3872afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
3877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
3879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
3882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
3883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
3884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
3885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
3888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
3889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
3890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
3893afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3894afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                where = FileColumns.MEDIA_TYPE + "=" + FileColumns.MEDIA_TYPE_VIDEO;
3895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
3898afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                out.table = "files";
3899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
3900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
3901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3902b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
3903b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
3904b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
3905b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
3906b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
3907b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
390816dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES_ID:
3909e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS_ID:
39101717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                where = "_id=" + uri.getPathSegments().get(2);
391116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood            case FILES:
3912e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood            case MTP_OBJECTS:
391316dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood                out.table = "files";
39141717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood                break;
39151717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood
3916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
3917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
3919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
3922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
3923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
3924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
3925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
3926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
3927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
3928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
3929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
3930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
3934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
393501e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        uri = safeUncanonicalize(uri);
3936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
3937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
3938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
3940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
3941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
3942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
3943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
394410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper database = getDatabaseForUri(
394510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    Uri.parse("content://media/" + mMediaScannerVolume + "/audio"));
394610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (database == null) {
394710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                Log.w(TAG, "no database for scanned volume " + mMediaScannerVolume);
394810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            } else {
394910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                database.mScanStopTime = SystemClock.currentTimeMicro();
3950988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                String msg = dump(database, false);
3951988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                logToDb(database.getWritableDatabase(), msg);
395210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
3953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
3954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
3955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
3956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3957819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        if (match == VOLUMES_ID) {
3958819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            detachVolume(uri);
3959819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood            count = 1;
3960819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else if (match == MTP_CONNECTED) {
396134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            synchronized (mMtpServiceConnection) {
396234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                if (mMtpService != null) {
396334be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // MTP has disconnected, so release our connection to MtpService
396434be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    getContext().unbindService(mMtpServiceConnection);
396534be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 1;
396634be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // mMtpServiceConnection.onServiceDisconnected might not get called,
396734be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    // so set mMtpService = null here
396834be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    mMtpService = null;
396934be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                } else {
397034be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                    count = 0;
397134be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood                }
397234be4a25e4374a934ac4627fb5fed06bbf6f72faMike Lockwood            }
3973819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        } else {
3974b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey            final String volumeName = getVolumeName(uri);
3975b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
3976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
3977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
3978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
3979819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood                        "Unknown URI: " + uri + " match: " + match);
3980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
398110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            database.mNumDeletes++;
3982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
3983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
3985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
3986a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                if (sGetTableAndWhereParam.table.equals("files")) {
3987d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    String deleteparam = uri.getQueryParameter(MediaStore.PARAM_DELETE_DATA);
3988d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                    if (deleteparam == null || ! deleteparam.equals("false")) {
3989d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        database.mNumQueries++;
3990d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
3991d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sMediaTypeDataId,
3992d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
3993d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        String [] idvalue = new String[] { "" };
39944eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        String [] playlistvalues = new String[] { "", "" };
3995d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        while (c.moveToNext()) {
3996b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            final int mediaType = c.getInt(0);
3997b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            final String data = c.getString(1);
3998b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            final long id = c.getLong(2);
3999b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
4000b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            if (mediaType == FileColumns.MEDIA_TYPE_IMAGE) {
4001b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                deleteIfAllowed(uri, data);
4002b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName,
4003b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                        FileColumns.MEDIA_TYPE_IMAGE, id);
4004b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
4005b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                idvalue[0] = String.valueOf(id);
4006b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                database.mNumQueries++;
4007b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                Cursor cc = db.query("thumbnails", sDataOnlyColumn,
4008b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                        "image_id=?", idvalue, null, null, null);
4009b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                while (cc.moveToNext()) {
4010b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                    deleteIfAllowed(uri, cc.getString(0));
4011a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                                }
4012b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                cc.close();
4013b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                database.mNumDeletes++;
4014b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                db.delete("thumbnails", "image_id=?", idvalue);
4015b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
4016b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            } else if (mediaType == FileColumns.MEDIA_TYPE_VIDEO) {
4017b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                deleteIfAllowed(uri, data);
4018b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                MediaDocumentsProvider.onMediaStoreDelete(getContext(), volumeName,
4019b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                        FileColumns.MEDIA_TYPE_VIDEO, id);
4020b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
4021b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            } else if (mediaType == FileColumns.MEDIA_TYPE_AUDIO) {
4022f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                if (!database.mInternal) {
4023b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                    MediaDocumentsProvider.onMediaStoreDelete(getContext(),
4024b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                            volumeName, FileColumns.MEDIA_TYPE_AUDIO, id);
4025b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
4026b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                                    idvalue[0] = String.valueOf(id);
40279b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                    database.mNumDeletes += 2; // also count the one below
4028f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                    db.delete("audio_genres_map", "audio_id=?", idvalue);
40294eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    // for each playlist that the item appears in, move
40304eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    // all the items behind it forward by one
40314eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    Cursor cc = db.query("audio_playlists_map",
40324eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                            sPlaylistIdPlayOrder,
40334eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                            "audio_id=?", idvalue, null, null, null);
40344eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    while (cc.moveToNext()) {
40354eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        playlistvalues[0] = "" + cc.getLong(0);
40364eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        playlistvalues[1] = "" + cc.getInt(1);
40379b1fff741d2dc2b5f64727417c1ae74a051f9b02Marco Nelissen                                        database.mNumUpdates++;
40384eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                        db.execSQL("UPDATE audio_playlists_map" +
40394eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                " SET play_order=play_order-1" +
40404eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                " WHERE playlist_id=? AND play_order>?",
40414eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                                playlistvalues);
40424eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    }
40434eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                                    cc.close();
4044f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                    db.delete("audio_playlists_map", "audio_id=?", idvalue);
4045f0b229bef169b9c7197ec630ef79a36bf5ba9969Marco Nelissen                                }
4046b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                            } else if (mediaType == FileColumns.MEDIA_TYPE_PLAYLIST) {
4047d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                // TODO, maybe: remove the audio_playlists_cleanup trigger and implement
4048d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                                // it functionality here (clean up the playlist map)
40495afff430a4ebb751dadeb0112a1fe2052c6f5c05Marco Nelissen                            }
4050a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                        }
4051d1983eb3406c26342fd5da4657e819a905fd4bebMarco Nelissen                        c.close();
4052a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    }
4053a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                }
4054a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen
4055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
405636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                    case MTP_OBJECTS:
4057e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood                    case MTP_OBJECTS_ID:
4058d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        try {
4059d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            // don't send objectRemoved event since this originated from MTP
4060d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = true;
406110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            database.mNumDeletes++;
406210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            count = db.delete("files", sGetTableAndWhereParam.where, whereArgs);
4063d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        } finally {
4064d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                            mDisableMtpObjectCallbacks = false;
4065d186c64cdd590e6491ee46dd8fcd52600d2edc5cMike Lockwood                        }
406636339ae4a18855b6e26d82db9d3afd26dc6150a1Mike Lockwood                        break;
406778b2885edc406273d688536b0eadfea006b20662Marco Nelissen                    case AUDIO_GENRES_ID_MEMBERS:
406810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
406978b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        count = db.delete("audio_genres_map",
407078b2885edc406273d688536b0eadfea006b20662Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
407178b2885edc406273d688536b0eadfea006b20662Marco Nelissen                        break;
4072166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
4073a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS_ID:
4074a6207286f6e1cc4a13d44194f25ecfc40796e024Marco Nelissen                    case IMAGES_THUMBNAILS:
4075166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS_ID:
4076166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                    case VIDEO_THUMBNAILS:
4077166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        // Delete the referenced files first.
4078166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        Cursor c = db.query(sGetTableAndWhereParam.table,
4079166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sDataOnlyColumn,
4080166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs, null, null, null);
4081166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        if (c != null) {
4082166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            while (c.moveToNext()) {
4083b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen                                deleteIfAllowed(uri, c.getString(0));
4084166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            }
4085166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                            c.close();
4086166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        }
4087166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        database.mNumDeletes++;
4088166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        count = db.delete(sGetTableAndWhereParam.table,
4089166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                                sGetTableAndWhereParam.where, whereArgs);
4090166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen                        break;
4091166204590e5f58008dbc0b4d3abdfa7ab4619867Marco Nelissen
4092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
409310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        database.mNumDeletes++;
4094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
4095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
4096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
4097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4098b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
40993631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // Since there are multiple Uris that can refer to the same files
41003631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // and deletes can affect other objects in storage (like subdirectories
41013631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // or playlists) we will notify a change on the entire volume to make
41023631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                // sure no listeners miss the notification.
4103b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey                Uri notifyUri = Uri.parse("content://" + MediaStore.AUTHORITY + "/" + volumeName);
41043631d46b679a64a16918698121916b60d7c86e97Mike Lockwood                getContext().getContentResolver().notifyChange(notifyUri, null);
4105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
4109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
411238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    public Bundle call(String method, String arg, Bundle extras) {
411338b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        if (MediaStore.UNHIDE_CALL.equals(method)) {
411438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            processRemovedNoMediaPath(arg);
411538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen            return null;
411638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        }
411738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen        throw new UnsupportedOperationException("Unsupported call: " + method);
411838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    }
411938b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen
412038b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen    @Override
4121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
4122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
412301e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        uri = safeUncanonicalize(uri);
4124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
4125b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
4126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
412710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper = getDatabaseForUri(uri);
412810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        if (helper == null) {
4129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
4130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
4131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
413210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumUpdates++;
413310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
413410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        SQLiteDatabase db = helper.getWritableDatabase();
4135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4136b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        String genre = null;
4137b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        if (initialValues != null) {
4138b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            genre = initialValues.getAsString(Audio.AudioColumns.GENRE);
4139b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood            initialValues.remove(Audio.AudioColumns.GENRE);
4140b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood        }
4141b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood
4142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
4143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
4144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
41451d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // special case renaming directories via MTP.
41461d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // in this case we must update all paths in the database with
41471d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            // the directory name as a prefix
41481d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            if ((match == MTP_OBJECTS || match == MTP_OBJECTS_ID)
41491d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    && initialValues != null && initialValues.size() == 1) {
41501d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                String oldPath = null;
4151801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                String newPath = initialValues.getAsString(MediaStore.MediaColumns.DATA);
41527f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                mDirectoryCache.remove(newPath);
41531d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                // MtpDatabase will rename the directory first, so we test the new file name
415438b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                File f = new File(newPath);
415538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                if (newPath != null && f.isDirectory()) {
415610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumQueries++;
41571d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    Cursor cursor = db.query(sGetTableAndWhereParam.table, PATH_PROJECTION,
41581d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        userWhere, whereArgs, null, null, null);
41591d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    try {
41601d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null && cursor.moveToNext()) {
41611d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            oldPath = cursor.getString(1);
41621d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
41631d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    } finally {
41641d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (cursor != null) cursor.close();
41651d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
41661d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    if (oldPath != null) {
41677f36494e085c26c69cd5925e54028822025eff29Marco Nelissen                        mDirectoryCache.remove(oldPath);
41681d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        // first rename the row for the directory
416910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
41701d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        count = db.update(sGetTableAndWhereParam.table, initialValues,
41711d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
41721d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0) {
41739bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                            // update the paths of any files and folders contained in the directory
41743fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                            Object[] bindArgs = new Object[] {
41753fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    newPath,
41763fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    oldPath.length() + 1,
41773fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    oldPath + "/",
41783fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    oldPath + "0",
41799bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    // update bucket_display_name and bucket_id based on new path
41809bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    f.getName(),
41819bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    f.toString().toLowerCase().hashCode()
41829bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    };
418310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
41845461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                            db.execSQL("UPDATE files SET _data=?1||SUBSTR(_data, ?2)" +
41859bc0e811f06c871f8263855bcff32bc8e58d8d20Mike Lockwood                                    // also update bucket_display_name
41863fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    ",bucket_display_name=?5" +
41873fa4c1f0f2d9631aa41567b6c2efb2716421ba40Marco Nelissen                                    ",bucket_id=?6" +
4188a82930532f35f34da696dd00f1f511fcdbb56c8arepo sync                                    " WHERE _data >= ?3 AND _data < ?4;",
41895461887800a42bdcbcaaad5d2da0bf97ea867b7aMarco Nelissen                                    bindArgs);
41901d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
41911d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
41921d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        if (count > 0 && !db.inTransaction()) {
41931d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                            getContext().getContentResolver().notifyChange(uri, null);
41941d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        }
419538b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        if (f.getName().startsWith(".")) {
419638b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            // the new directory name is hidden
419738b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                            processNewNoMediaPath(helper, db, newPath);
419838b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                        }
41991d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                        return count;
42001d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                    }
420138b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                } else if (newPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
420238b4364a731875c8f5a845f7543da3494a5424d0Marco Nelissen                    processNewNoMediaPath(helper, db, newPath);
42031d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood                }
42041d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            }
42051d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
4206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
4207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
4208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
4209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
4210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
42112658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String albumartist = values.getAsString(MediaStore.Audio.Media.ALBUM_ARTIST);
42122658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        String compilation = values.getAsString(MediaStore.Audio.Media.COMPILATION);
42132658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                        values.remove(MediaStore.Audio.Media.COMPILATION);
421407656cccafca173c6ab54c681a69538dcf0516ddMarco Nelissen
4215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
4216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
4217a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
42186006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("artist");
4219a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
4220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
422110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> artistCache = helper.mArtistCache;
4222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
4223a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
4224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
422510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    artistRowId = getKeyIdForName(helper, db,
422610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "artists", "artist_key", "artist",
4227acfb9a20e1131f7dc2521331ee3856c8586c35bdMarco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
4228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
4229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
4230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
4231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
4233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
423559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
4236a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
42376006570c27674c47fcea8e674832a715cfd653eaMarco Nelissen                        values.remove("album");
4238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
4239801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                            String path = values.getAsString(MediaStore.MediaColumns.DATA);
424059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
42412658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            if (albumartist != null) {
42422658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                albumHash = albumartist.hashCode();
42432658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                            } else if (compilation != null && compilation.equals("1")) {
42442658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen                                // nothing to do, hash already set
424559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
42469289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path == null) {
42479289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    if (match == AUDIO_MEDIA) {
42489289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Log.w(TAG, "Possible multi row album name update without"
42499289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                + " path could give wrong album key");
42509289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    } else {
42519289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        //Log.w(TAG, "Specify path to avoid extra query");
42529289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        Cursor c = query(uri,
42539289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                new String[] { MediaStore.Audio.Media.DATA},
42549289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                null, null, null);
42559289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        if (c != null) {
42569289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            try {
42579289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                int numrows = c.getCount();
42589289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                if (numrows == 1) {
42599289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    c.moveToFirst();
42609289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    path = c.getString(0);
42619289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                } else {
42629289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                    Log.e(TAG, "" + numrows + " rows for " + uri);
42639289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                }
42649289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            } finally {
42659289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                                c.close();
42669289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                            }
42679289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                        }
42689289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    }
42699289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
42709289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                if (path != null) {
42719289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                    albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
42729289cbe6396b92365563206050caf3a5efb1f5c8Marco Nelissen                                }
427359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
42742658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen
4275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
4276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
427710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            HashMap<String, Long> albumCache = helper.mAlbumCache;
4278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
427959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
428059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
4281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
428210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                    albumRowId = getKeyIdForName(helper, db,
428310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                                            "albums", "album_key", "album",
4284a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
4285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
4286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
4287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
4288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
4290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
4293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
4294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
4295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
4296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
4297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
4298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
4299e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
4300e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
4301e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
4302e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
4303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
430510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
4306afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                        count = db.update(sGetTableAndWhereParam.table, values,
4307afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood                                sGetTableAndWhereParam.where, whereArgs);
4308b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        if (genre != null) {
4309b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            if (count == 1 && match == AUDIO_MEDIA_ID) {
4310b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                long rowId = Long.parseLong(uri.getPathSegments().get(3));
4311b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                updateGenre(rowId, genre);
4312b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            } else {
4313b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                // can't handle genres for bulk update or for non-audio files
4314b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                Log.w(TAG, "ignoring genre in update: count = "
4315b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                                        + count + " match = " + match);
4316b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                            }
4317b8f9b763105fb19dc4d955edc3095a64b94d2a58Mike Lockwood                        }
4318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
4321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
4322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
4323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
4324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
4325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
4326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
4327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
4328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
4329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
4330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
4331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
4332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
4333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
4334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
4335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4336b9842a182cb761dbcac82ff2024e38d0cd9a9e73Ray Chen                        computeTakenTime(values);
433710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumUpdates++;
4338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
4339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
434001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
434101a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
434201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
434301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
434410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumQueries++;
434501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
434601a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
434701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
4348b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
4349216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                try {
4350216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    while (c.moveToNext()) {
4351216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        long magic = c.getLong(2);
4352216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        if (magic == 0) {
4353216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                            requestMediaThumbnail(c.getString(1), uri,
4354216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                                    MediaThumbRequest.PRIORITY_NORMAL, 0);
4355216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                        }
4356b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
4357216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                } finally {
4358216ec3f0643cd9a9ea9daf96a0ed98d1cc4b1751Marco Nelissen                                    c.close();
4359b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
4360b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
4361b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
4362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4364f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4365f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
4366f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    String moveit = uri.getQueryParameter("move");
4367f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    if (moveit != null) {
4368f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
4369f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        if (initialValues.containsKey(key)) {
4370f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int newpos = initialValues.getAsInteger(key);
4371f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            List <String> segments = uri.getPathSegments();
4372f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            long playlist = Long.valueOf(segments.get(3));
4373f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int oldpos = Integer.valueOf(segments.get(5));
437410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            return movePlaylistEntry(helper, db, playlist, oldpos, newpos);
4375f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        }
4376f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        throw new IllegalArgumentException("Need to specify " + key +
4377f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                                " when using 'move' parameter");
4378f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    }
4379f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    // fall through
4380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
438110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper.mNumUpdates++;
4382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
4383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
4384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
4385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
4386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4387cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // in a transaction, the code that began the transaction should be taking
4388cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // care of notifications once it ends the transaction successfully
4389cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (count > 0 && !db.inTransaction()) {
4390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
4391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
4393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
439510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private int movePlaylistEntry(DatabaseHelper helper, SQLiteDatabase db,
439610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            long playlist, int from, int to) {
4397f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        if (from == to) {
4398f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return 0;
4399f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
4400f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        db.beginTransaction();
44015e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        int numlines = 0;
4402f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        try {
440310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            helper.mNumUpdates += 3;
44044eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            Cursor c = db.query("audio_playlists_map",
44054eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    new String [] {"play_order" },
44064eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order",
44074eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    from + ",1");
44084eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.moveToFirst();
44094eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            int from_play_order = c.getInt(0);
44104eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.close();
44114eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c = db.query("audio_playlists_map",
44124eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    new String [] {"play_order" },
44134eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    "playlist_id=?", new String[] {"" + playlist}, null, null, "play_order",
44144eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    to + ",1");
44154eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.moveToFirst();
44164eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            int to_play_order = c.getInt(0);
44174eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            c.close();
4418f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=-1" +
44194eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                    " WHERE play_order=" + from_play_order +
4420f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " AND playlist_id=" + playlist);
4421f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // We could just run both of the next two statements, but only one of
4422f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // of them will actually do anything, so might as well skip the compile
4423f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // and execute steps.
4424f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            if (from  < to) {
4425f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" +
44264eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " WHERE play_order<=" + to_play_order +
44274eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " AND play_order>" + from_play_order +
4428f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4429f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = to - from + 1;
4430f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            } else {
4431f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" +
44324eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " WHERE play_order>=" + to_play_order +
44334eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen                        " AND play_order<" + from_play_order +
4434f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
4435f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = from - to + 1;
4436f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            }
44374eff7fef57715b2247f9cfc98c7f69809ab35adeMarco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=" + to_play_order +
4438f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=-1 AND playlist_id=" + playlist);
4439f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.setTransactionSuccessful();
4440f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        } finally {
4441f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.endTransaction();
4442f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
44435e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé
44445e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
44455e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé                .buildUpon().appendEncodedPath(String.valueOf(playlist)).build();
44465e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        // notifyChange() must be called after the database transaction is ended
44475e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        // or the listeners will read the old data in the callback
44485e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        getContext().getContentResolver().notifyChange(uri, null);
44495e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé
44505e7f74c68fd89c385bc1f4a08e4ddd439c930c7eOscar Rydhé        return numlines;
4451f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    }
4452f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
4453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
4454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
4455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
4456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
4458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
4459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
446071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
446101e706a024fc4ac6865718922ebceef6dfef54e1Marco Nelissen        uri = safeUncanonicalize(uri);
4462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
446371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
446471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
446571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
446671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
446771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
446871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
446971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
447071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
44715fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            if (db == null) {
44725fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                throw new IllegalStateException("Couldn't open database for " + uri);
44735fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang            }
447471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
447571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
447671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
447771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
447871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
447971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
448071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
448171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
448271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
448371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
448471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                String audiopath = c.getString(0);
448571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                int albumid = c.getInt(1);
448671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // Try to get existing album art for this album first, which
448771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // could possibly have been obtained from a different file.
448871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // If that fails, try to get it from this specific file.
448971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
449071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
4491007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    pfd = openFileAndEnforcePathPermissionsHelper(newUri, mode);
449271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
449371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    // That didn't work, now try to get it from the specific file
449434fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen                    pfd = getThumb(database, db, audiopath, albumid, null);
449571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
449671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
449771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            c.close();
449871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
449971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
450071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4502007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            pfd = openFileAndEnforcePathPermissionsHelper(uri, mode);
4503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
450471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
450571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
450671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
450771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
450871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
4509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
4510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
4511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
4512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
4513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
4514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
45165fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                if (db == null) {
45175fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                    throw new IllegalStateException("Couldn't open database for " + uri);
45185fde670987a55d69442566dcbdb7830d5f1587c6Chih-Chung Chang                }
4519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
4520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
452171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
4522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
4523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
4524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
4525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
4526a4d7f8a140c9a66bfcb28c5197521db6d62e13beMarco Nelissen                        null, null, null, null, MediaStore.Audio.Media.TRACK);
452771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                if (c.moveToFirst()) {
4528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String audiopath = c.getString(0);
452934fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen                    pfd = getThumb(database, db, audiopath, albumid, uri);
4530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                c.close();
4532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
453371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
453471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
453571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
4536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
4537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
4538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4540007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /**
4541007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * Return the {@link MediaColumns#DATA} field for the given {@code Uri}.
4542007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     */
4543007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private File queryForDataFile(Uri uri) throws FileNotFoundException {
4544007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final Cursor cursor = query(
4545007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                uri, new String[] { MediaColumns.DATA }, null, null, null);
45468ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey        if (cursor == null) {
45478ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey            throw new FileNotFoundException("Missing cursor for " + uri);
45488ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey        }
45498ea255e83eb38aa6a33d0e02698149799f77672aJeff Sharkey
4550007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
4551007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            switch (cursor.getCount()) {
4552007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                case 0:
4553007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    throw new FileNotFoundException("No entry for " + uri);
4554007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                case 1:
4555007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    if (cursor.moveToFirst()) {
4556007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        return new File(cursor.getString(0));
4557007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    } else {
4558007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                        throw new FileNotFoundException("Unable to read entry for " + uri);
4559007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    }
4560007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                default:
4561007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey                    throw new FileNotFoundException("Multiple items at " + uri);
4562007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            }
4563007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } finally {
4564007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            cursor.close();
4565007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4566007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
4567007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4568007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    /**
4569007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * Replacement for {@link #openFileHelper(Uri, String)} which enforces any
4570007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     * permissions applicable to the path before returning.
4571007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey     */
4572007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    private ParcelFileDescriptor openFileAndEnforcePathPermissionsHelper(Uri uri, String mode)
4573007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throws FileNotFoundException {
4574a2bd8dfeac57dc09727c6d16ebf9bac1061df23dAdam Lesinski        final int modeBits = ParcelFileDescriptor.parseMode(mode);
4575007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4576f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey        File file = queryForDataFile(uri);
4577b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4578b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        checkAccess(uri, file, modeBits);
4579b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4580b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        // Bypass emulation layer when file is opened for reading, but only
4581b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        // when opening read-only and we have an exact match.
4582b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        if (modeBits == MODE_READ_ONLY) {
4583b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            file = Environment.maybeTranslateEmulatedPathToInternal(file);
4584b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        }
4585b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4586b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        return ParcelFileDescriptor.open(file, modeBits);
4587b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen    }
4588b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4589b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen    private void deleteIfAllowed(Uri uri, String path) {
4590b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        try {
4591b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            File file = new File(path);
4592b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            checkAccess(uri, file, ParcelFileDescriptor.MODE_WRITE_ONLY);
4593b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            file.delete();
4594b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        } catch (Exception e) {
4595b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen            Log.e(TAG, "Couldn't delete " + path);
4596b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        }
4597b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen    }
4598b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen
4599b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen    private void checkAccess(Uri uri, File file, int modeBits) throws FileNotFoundException {
4600b2c3695f8a7303469db2909e3ad8fdc090b99caaMarco Nelissen        final boolean isWrite = (modeBits & MODE_WRITE_ONLY) != 0;
4601007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        final String path;
4602007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        try {
4603007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            path = file.getCanonicalPath();
4604007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } catch (IOException e) {
4605007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            throw new IllegalArgumentException("Unable to resolve canonical path for " + file, e);
4606007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4607007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
46086ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen        Context c = getContext();
46096ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen        boolean readGranted =
46106ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen                (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
46116ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen                == PackageManager.PERMISSION_GRANTED);
46126ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen
46135943bf87fe6511fa688cb29ccef87ace5ccda522Marco Nelissen        if (path.startsWith(sExternalPath) || path.startsWith(sLegacyPath)) {
46146ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            if (!readGranted) {
46153e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                c.enforceCallingOrSelfPermission(
46163e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                        READ_EXTERNAL_STORAGE, "External path: " + path);
46173e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen            }
4618007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4619007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            if (isWrite) {
46203e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                if (c.checkCallingOrSelfUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
46213e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                        != PackageManager.PERMISSION_GRANTED) {
46223e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                    c.enforceCallingOrSelfPermission(
46233e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                            WRITE_EXTERNAL_STORAGE, "External path: " + path);
46243e6a42887a17a93b8906ff05693587e25158f2b1Marco Nelissen                }
4625007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey            }
4626f3329452c5554f4a3bcd3c41d7ec669a0d55c997Jeff Sharkey
4627007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        } else if (path.startsWith(sCachePath)) {
46286ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            if (!readGranted) {
46296ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen                c.enforceCallingOrSelfPermission(
46306ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen                        ACCESS_CACHE_FILESYSTEM, "Cache path: " + path);
46316ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            }
463270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        } else if (isWrite) {
463370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            // don't write to non-cache, non-sdcard files.
463470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            throw new FileNotFoundException("Can't access " + file);
4635dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen        } else if (isSecondaryExternalPath(path)) {
4636dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen            // read access is OK with the appropriate permission
46376ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            if (!readGranted) {
46386ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen                c.enforceCallingOrSelfPermission(
46396ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen                        READ_EXTERNAL_STORAGE, "External path: " + path);
46406ab6373dea9c980c41c1cb1d5f59e0fd78bbf8a7Marco Nelissen            }
464170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        } else {
464270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            checkWorldReadAccess(path);
4643007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey        }
4644007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey    }
4645007645ee2c67b83ffdd90d0e583ec866c60c4614Jeff Sharkey
4646dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen    private boolean isSecondaryExternalPath(String path) {
4647dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen        for (int i = mExternalStoragePaths.length - 1; i >= 0; --i) {
4648dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen            if (path.startsWith(mExternalStoragePaths[i])) {
4649dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen                return true;
4650dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen            }
4651dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen        }
4652dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen        return false;
4653dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen    }
4654dc906ab2d86f6138cd29a25b432c91835702244fMarco Nelissen
465570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    /**
465670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen     * Check whether the path is a world-readable file
465770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen     */
465870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    private void checkWorldReadAccess(String path) throws FileNotFoundException {
465970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
466070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        try {
466170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            StructStat stat = Libcore.os.stat(path);
466270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            int accessBits = OsConstants.S_IROTH;
466370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            if (OsConstants.S_ISREG(stat.st_mode) &&
466470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                ((stat.st_mode & accessBits) == accessBits)) {
466570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                checkLeadingPathComponentsWorldExecutable(path);
466670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                return;
466770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
466870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        } catch (ErrnoException e) {
466970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            // couldn't stat the file, either it doesn't exist or isn't
467070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            // accessible to us
467170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        }
467270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
467370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        throw new FileNotFoundException("Can't access " + path);
467470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    }
467570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
467670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    private void checkLeadingPathComponentsWorldExecutable(String filePath)
467770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            throws FileNotFoundException {
467870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        File parent = new File(filePath).getParentFile();
467970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
468070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        int accessBits = OsConstants.S_IXOTH;
468170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
468270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        while (parent != null) {
468370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            if (! parent.exists()) {
468470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                // parent dir doesn't exist, give up
468570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                throw new FileNotFoundException("access denied");
468670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
468770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            try {
468870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                StructStat stat = Libcore.os.stat(parent.getPath());
468970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                if ((stat.st_mode & accessBits) != accessBits) {
469070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                    // the parent dir doesn't have the appropriate access
469170eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                    throw new FileNotFoundException("Can't access " + filePath);
469270eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                }
469370eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            } catch (ErrnoException e1) {
469470eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                // couldn't stat() parent
469570eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen                throw new FileNotFoundException("Can't access " + filePath);
469670eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            }
469770eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen            parent = parent.getParentFile();
469870eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen        }
469970eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen    }
470070eadbf4eb99cd373d23ac3bb988c1e94e445927Marco Nelissen
4701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
470210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        DatabaseHelper helper;
4703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
4704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
4705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
4706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
4707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
470910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private void makeThumbAsync(DatabaseHelper helper, SQLiteDatabase db,
471010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String path, long album_id) {
47118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
47128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
47138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
47148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
47158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
47168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
47178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
47198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
47208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
4721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
472210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        d.helper = helper;
4723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
4724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
4725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
4726a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
47278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
47298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
47308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
47318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
47328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
47338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
47348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
47358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
47368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
47378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
4738b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
4739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
4740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
4741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4742f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    //Return true if the artPath is the dir as it in mExternalStoragePaths
4743f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    //for multi storage support
4744f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    private static boolean isRootStorageDir(String artPath) {
4745f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        for ( int i = 0; i < mExternalStoragePaths.length; i++) {
4746f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen            if ((mExternalStoragePaths[i] != null) &&
4747f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                    (artPath.equalsIgnoreCase(mExternalStoragePaths[i])))
4748f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                return true;
4749f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        }
4750f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen        return false;
4751f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen    }
4752f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen
47538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
47548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
47558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
47568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
4757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
4759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
4760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
4761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
4762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
47638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
47648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
4765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
4766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
4767d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
47683f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // same directory as the media file, except if that directory is
47693f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // is the root directory of the sd card or the download directory.
4770d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
4771d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
4772d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
4773d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
4774d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
4775d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
47768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
4777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
4778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
4779d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
47803f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String artPath = path.substring(0, lastSlash);
47810fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen                    String dwndir = Environment.getExternalStoragePublicDirectory(
47822f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
4783d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4784d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
4785d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
4786d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
4787d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
4788f5b95460c7b93029bf16634dbefe4185d461410aguoyin.chen                        } else if (!isRootStorageDir(artPath) &&
4789ad189fb4a03da9a9ef4125207421ad755269d6f8Marco Nelissen                                !artPath.equalsIgnoreCase(dwndir)) {
4790d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
4791d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
4792d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
4793d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
4794d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4795d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
4796d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
4797d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
4798d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
4799d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
4800d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4801d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
4802d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
4803d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
4804d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
4805d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4806d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
4807d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
4808d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
4809d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
4810d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4811d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
4812d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
4813d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4814d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
4815d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
4816d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
4817d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
4818d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4819d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
4820d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
4821d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
4822d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
4823d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
4824d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
4825d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
48263f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        File file = new File(artPath, bestmatch);
4827d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
4828d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
4829d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
48302c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                compressed = new byte[(int)file.length()];
4831d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
4832d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
4833d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
4834d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
48352c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                            } catch (OutOfMemoryError ex) {
48362c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                Log.w(TAG, ex);
48372c3a3d48a78c794c304ed4345d6fa7089d6b1579Birdson Chen                                compressed = null;
4838d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
4839d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
4840d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
4841d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
4842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
4843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
4844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
4845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
48478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
48488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
4849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
48508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
48518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
4852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
48538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
485410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    Uri getAlbumArtOutputUri(DatabaseHelper helper, SQLiteDatabase db, long album_id, Uri albumart_uri) {
48558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
48568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
48578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
48588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
4859801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood            Cursor c = query(albumart_uri, new String [] { MediaStore.MediaColumns.DATA },
48608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
4861d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            try {
4862d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null && c.moveToFirst()) {
4863d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    String albumart_path = c.getString(0);
4864d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    if (ensureFileExists(albumart_path)) {
4865d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                        out = albumart_uri;
4866d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    }
4867d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                } else {
4868d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    albumart_uri = null;
4869d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                }
4870d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson            } finally {
4871d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                if (c != null) {
4872d8c100241c1c8983deeae6329111e2bce997294dJoakim Johansson                    c.close();
4873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
4874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
487571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
487671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
48778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
48788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
48798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
48808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
488110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper.mNumInserts++;
4882801ba04ac37ee06dccb8bf4081b29a162795e3a6Mike Lockwood                long rowId = db.insert("album_art", MediaStore.MediaColumns.DATA, values);
48838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
48848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
488550c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen                    // ensure the parent directory exists
488650c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen                    String albumart_path = values.getAsString(MediaStore.MediaColumns.DATA);
488750c62ef29017f94bd7b83dd4650aebb23be477dfMarco Nelissen                    ensureFileExists(albumart_path);
4888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
48898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
48908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
48918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
48928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
48938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
48948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
48958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
48968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
48978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
48988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
489920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) throws IOException {
490020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen        OutputStream outstream = null;
49018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
490220e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            outstream = getContext().getContentResolver().openOutputStream(out);
49038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
49058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
49068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
49078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
49088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
490920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                if (!bm.compress(Bitmap.CompressFormat.JPEG, 85, outstream)) {
491020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    throw new IOException("failed to compress bitmap");
491120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                }
4912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
491320e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen        } finally {
491420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            IoUtils.closeQuietly(outstream);
4915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
49168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
49178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
491834fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen    private ParcelFileDescriptor getThumb(DatabaseHelper helper, SQLiteDatabase db, String path,
491934fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen            long album_id, Uri albumart_uri) {
492071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
492134fa8b5146353268880de4dcb61bb4fdffc10543Marco Nelissen        d.helper = helper;
492271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
492371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
492471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
492571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
492671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
492771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
492871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
492971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
49308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
4931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
49328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
493371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
49348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
49358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
49378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
49388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
49408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
49418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
49428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
49438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
49448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
49458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
494770676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final Resources r = getContext().getResources();
494870676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            final int maximumThumbSize = r.getDimensionPixelSize(R.dimen.maximum_thumb_size);
494970676508bd3a081edd909875e141f2c48d79c1acDaniel Lehmann            while (opts.outHeight > maximumThumbSize || opts.outWidth > maximumThumbSize) {
49508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
49518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
49528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
49538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
49548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
49568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
49578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
49588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
49598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
49608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
49618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
49628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
49638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
49648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
4966a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false);
4967a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    if (nbm != null && nbm != bm) {
4968a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm.recycle();
4969a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm = nbm;
4970a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    }
49718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
49728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
49738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
49748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
49758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
49768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
497771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
49788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
49798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
498071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
498171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
498271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
498371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
498424001394f571b1f0378840cbf299288e4df10508Bjorn Bringert                return ParcelFileDescriptor.fromData(compressed, "albumthumb");
498571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
498671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
498771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
4988a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This one needs to actually be saved on the sd card.
4989a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This is wrapped in a transaction because there are various things
4990a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // that could go wrong while generating the thumbnail, and we only want
4991a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // to update the database when all steps succeeded.
4992a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            d.db.beginTransaction();
499320e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            Uri out = null;
499420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            ParcelFileDescriptor pfd = null;
4995a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            try {
499620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                out = getAlbumArtOutputUri(d.helper, d.db, d.album_id, d.albumart_uri);
4997a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen
4998a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (out != null) {
4999a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    writeAlbumArt(need_to_recompress, out, compressed, bm);
5000a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    getContext().getContentResolver().notifyChange(MEDIA_URI, null);
500120e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    pfd = openFileHelper(out, "r");
5002a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    d.db.setTransactionSuccessful();
5003a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    return pfd;
5004a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                }
500520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen            } catch (IOException ex) {
5006a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
5007a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (UnsupportedOperationException ex) {
5008a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
5009a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } finally {
5010a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                d.db.endTransaction();
5011a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (bm != null) {
5012a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    bm.recycle();
501371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
501420e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                if (pfd == null && out != null) {
501520e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // Thumbnail was not written successfully, delete the entry that refers to it.
501620e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // Note that this only does something if getAlbumArtOutputUri() reused an
501720e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // existing entry from the database. If a new entry was created, it will
501820e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    // have been rolled back as part of backing out the transaction.
501920e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                    getContext().getContentResolver().delete(out, null, null);
502020e23658bac72ce379583ea32a006c0f093197e3Marco Nelissen                }
502171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
50228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
502371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
5024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
5028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
5029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
5030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
5031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
5032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
5033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
503459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
503559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
503659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
5037a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
5038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
5039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
5040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
5041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
5042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
504310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    private long getKeyIdForName(DatabaseHelper helper, SQLiteDatabase db,
504410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            String table, String keyField, String nameField,
504559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
5046a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
5047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
5048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
505051cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            rawName = MediaStore.UNKNOWN_STRING;
5051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
5053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
505551cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            // shouldn't happen, since we only get null keys for null inputs
505651cba5e1acf1c56be3dc6c7c46a73a5a0409b452Marco Nelissen            Log.e(TAG, "null key", new Exception());
5057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
5058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
506059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
5061e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen        boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName);
506259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
50632658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // To distinguish same-named albums, we append a hash. The hash is based
50642658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // on the "album artist" tag if present, otherwise on the "compilation" tag
50652658ef46ac41dfd83e385cee5ab96d7f8acbdec5Marco Nelissen        // if present, otherwise on the path.
506659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
506759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
506859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
506959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
507059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
5071a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
507259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
5073a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
5074a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
5075a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
507659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
507759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
5078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
507910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        helper.mNumQueries++;
5080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
5081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
5083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
5084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
5085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
5086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
5087702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
5088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
508910af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        helper.mNumInserts++;
5090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
509159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
5092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
509310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            makeThumbAsync(helper, db, path, rowId);
5094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
5095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
5096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
5097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
5098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
5099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
5100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
5101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
5102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
5103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
5104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
5105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
5106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
5108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
5109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
5110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
5111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
5112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
5113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
5114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
511510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                            helper.mNumUpdates++;
5116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
5117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
5118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
5119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
5120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
5121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
5122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
5123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
5124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
5125702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
5126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
5127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
5128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
5130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (c != null) c.close();
5131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
513359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
513459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
5135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
5137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
5141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
5142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
5143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
5144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
5145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
5147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
5148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
5149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
5150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
5152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
5153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
5154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
5156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
5157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
5159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
5160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
5161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
5162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
5163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
5164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
5165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
5166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
5167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
5171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
5172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
5173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
5174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
5175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
5176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
5179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
5180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
5185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
5187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
5188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
5190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
51915619dbb4338e9acea165f4cc0f1791d00e8df445Marco Nelissen            if (uri.getPathSegments().size() >= 1) {
5192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
5193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
5196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5198fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isMediaDatabaseName(String name) {
5199fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
5200fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5201fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5202fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (EXTERNAL_DATABASE_NAME.equals(name)) {
5203fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5204fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5205168d49e39a356cdbd0fa04c356e3480e413d6f34kwangjung.kim        if (name.startsWith("external-") && name.endsWith(".db")) {
5206fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5207fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5208fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
5209fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
5210fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
5211fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    static boolean isInternalMediaDatabaseName(String name) {
5212fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        if (INTERNAL_DATABASE_NAME.equals(name)) {
5213fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn            return true;
5214fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        }
5215fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn        return false;
5216fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn    }
5217fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn
5218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
5220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
5221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
5222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
5224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
5225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
522775392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
5228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
5229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
5230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
5233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
5234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
5235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5237993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood            Context context = getContext();
523810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            DatabaseHelper helper;
5239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
524010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true,
5241fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                        false, mObjectRemovedCallback);
5242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
5243993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                if (Environment.isExternalStorageRemovable()) {
52445d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    final StorageVolume actualVolume = mStorageManager.getPrimaryVolume();
52455d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    final int volumeId = actualVolume.getFatVolumeId();
5246993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood
5247ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // Must check for failure!
5248ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // If the volume is not (yet) mounted, this will create a new
5249ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // external-ffffffff.db database instead of the one we expect.  Then, if
5250ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // android.process.media is later killed and respawned, the real external
5251ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    // database will be attached, containing stale records, or worse, be empty.
52525d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    if (volumeId == -1) {
5253ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        String state = Environment.getExternalStorageState();
5254ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        if (Environment.MEDIA_MOUNTED.equals(state) ||
5255ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
5256ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // This may happen if external storage was _just_ mounted.  It may also
5257ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // happen if the volume ID is _actually_ 0xffffffff, in which case it
5258ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // must be changed since FileUtils::getFatVolumeId doesn't allow for
5259ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // that.  It may also indicate that FileUtils::getFatVolumeId is broken
5260ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            // (missing ioctl), which is also impossible to disambiguate.
5261ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            Log.e(TAG, "Can't obtain external volume ID even though it's mounted.");
5262ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        } else {
5263ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                            Log.i(TAG, "External volume is not (yet) mounted, cannot attach.");
5264ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        }
5265ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick
5266ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                        throw new IllegalArgumentException("Can't obtain external volume ID for " +
5267ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                                volume + " volume.");
5268ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick                    }
5269ae1e6c5632ff08bcdacb7c75662398db122ac850Mike Kasick
5270993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // generate database name based on volume ID
52715d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    String dbName = "external-" + Integer.toHexString(volumeId) + ".db";
527210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbName, false,
5273fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
52745d36def3fe5c6a7ec3d04e2f555c47bb4a8babedJeff Sharkey                    mVolumeId = volumeId;
5275993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                } else {
5276993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // external database name should be EXTERNAL_DATABASE_NAME
5277993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // however earlier releases used the external-XXXXXXXX.db naming
5278993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // for devices without removable storage, and in that case we need to convert
5279993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    // to this new convention
5280993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    File dbFile = context.getDatabasePath(EXTERNAL_DATABASE_NAME);
5281993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    if (!dbFile.exists()) {
5282993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // find the most recent external database and rename it to
5283993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // EXTERNAL_DATABASE_NAME, and delete any other older
5284993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // external database files
5285993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        File recentDbFile = null;
5286993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        for (String database : context.databaseList()) {
5287ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                            if (database.startsWith("external-") && database.endsWith(".db")) {
5288993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                File file = context.getDatabasePath(database);
5289993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                if (recentDbFile == null) {
5290993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
5291993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else if (file.lastModified() > recentDbFile.lastModified()) {
5292ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                                    context.deleteDatabase(recentDbFile.getName());
5293993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                    recentDbFile = file;
5294993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                } else {
5295ae6d97e6bc6a03385909f939be6e35e47520dc99kwangjung.kim                                    context.deleteDatabase(file.getName());
5296993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                }
5297993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
5298993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
5299993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        if (recentDbFile != null) {
5300993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            if (recentDbFile.renameTo(dbFile)) {
5301993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.d(TAG, "renamed database " + recentDbFile.getName() +
5302993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
5303993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            } else {
5304993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                Log.e(TAG, "Failed to rename database " + recentDbFile.getName() +
5305993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                        " to " + EXTERNAL_DATABASE_NAME);
5306993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // This shouldn't happen, but if it does, continue using
5307993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                // the file under its old name
5308993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                                dbFile = recentDbFile;
5309993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                            }
5310993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        }
5311993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                        // else DatabaseHelper will create one named EXTERNAL_DATABASE_NAME
5312993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                    }
531310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    helper = new DatabaseHelper(context, dbFile.getName(), false,
5314fd8402c8904368ad9e90a7fffe4237c87e11273cDianne Hackborn                            false, mObjectRemovedCallback);
5315993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood                }
5316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
5317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
5318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
532010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            mDatabases.put(volume, helper);
5321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
532210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            if (!helper.mInternal) {
5323ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood                // create default directories (only happens on first boot)
532410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                createDefaultFolders(helper, helper.getWritableDatabase());
5325ed9bbc4fa47d545b81248dd749aa0ee4fc598d25Mike Lockwood
5326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
53279be33f8b8588043b1d104d831fe600a6b7e9d63bMike Lockwood                File[] files = new File(mExternalStoragePaths[0], ALBUM_THUMB_FOLDER).listFiles();
5328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
5329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
5330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
5331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
5334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
5335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
5336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
5337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
5338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
5339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
5340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (cursor != null) cursor.close();
5341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
5344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
5345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
5346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
5347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
5348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
5349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
5353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
5354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
5357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
5358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
5359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
5360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
5361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
5362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
5363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
536475392afde5217038b0c98077758bb9329c2431b2Jeff Brown        if (Binder.getCallingPid() != Process.myPid()) {
5365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
5366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
5367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
5370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
5371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
5372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
5373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
5374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
5375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
5376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
5379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
5380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
5381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
5383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
5384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
5385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
5386e9ee0248d62f3badef8a554f35f78e9116ef8a5cMike Lockwood            } catch (Exception e) {
5387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
5388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
5389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
5391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
5392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
5393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
5395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
5396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
5397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
5399ae62a1d602e7ed2e0e30e271bddbb27aa71469f6Christian Mehlmauer    private static final boolean LOCAL_LOGV = false;
5400971a2ef5165e2072c76bf25049fdda94187019c2Dianne Hackborn
5401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
5402993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private static final String EXTERNAL_DATABASE_NAME = "external.db";
5403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
5405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
5406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
5408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
5409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
5410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
5412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
5414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
5416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
5417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
54180027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    // current FAT volume ID
5419993b6f0019bcc3d34f13d73ecef54621b7647d1cMike Lockwood    private int mVolumeId = -1;
54200027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
5421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
5422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
5423268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen    static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs";
5424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
5426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
5427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
54281717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // WARNING: the values of IMAGES_MEDIA, AUDIO_MEDIA, and VIDEO_MEDIA and AUDIO_PLAYLISTS
542916dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    // are stored in the "files" table, so do not renumber them unless you also add
54301717955cb3b68424ee7dbf7c644000cf82788253Mike Lockwood    // a corresponding database upgrade step for it.
5431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
5432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
5433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
5434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
5435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
5437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
5438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
5439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
5440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
5441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
5442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
5443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
5444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
5445bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen    private static final int AUDIO_GENRES_ALL_MEMBERS = 109;
5446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
5447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
5448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
5449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
5450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
5451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
5452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
5453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
5454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
5455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
5456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
545771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
5458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
5460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
5461b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
5462b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
5463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
5465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
5466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5467a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
5468a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
5469a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
5470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
5472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
54730027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private static final int FS_ID = 600;
5474704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen    private static final int VERSION = 601;
54750027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
547616dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES = 700;
547716dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood    private static final int FILES_ID = 701;
5478a36cfaef630ef5df7bef80b25f6bd493d040c7e4Brian Muramatsu
5479e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    // Used only by the MTP implementation
5480e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS = 702;
5481e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECTS_ID = 703;
5482e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood    private static final int MTP_OBJECT_REFERENCES = 704;
5483819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // UsbReceiver calls insert() and delete() with this URI to tell us
5484819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    // when MTP is connected and disconnected
5485819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood    private static final int MTP_CONNECTED = 705;
5486b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
5487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
5488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
5489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5490b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
5491b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
5492b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
5493b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
54941d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    private static final String[] PATH_PROJECTION = new String[] {
54951d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood        MediaStore.MediaColumns._ID,
54961d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood            MediaStore.MediaColumns.DATA,
54971d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood    };
54981d4a47c46bd6476f624f2fa41f99d28c44a2ab0dMike Lockwood
5499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
5500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
5501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
5502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
5503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5504b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
5505b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
5506b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
5507b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
5508b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
5509b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
5510e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood    private static final String OBJECT_REFERENCES_QUERY =
5511afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        "SELECT " + Audio.Playlists.Members.AUDIO_ID + " FROM audio_playlists_map"
5512afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " WHERE " + Audio.Playlists.Members.PLAYLIST_ID + "=?"
5513afa157c5799e410210af7d6b343cec8a9907afd2Mike Lockwood        + " ORDER BY " + Audio.Playlists.Members.PLAY_ORDER;
5514e2eb1e85bbac2675c78c92cb1181b652ccd0b0f5Mike Lockwood
5515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
5516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
5517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
5518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
5519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
5520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
5521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
5523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
5524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
5525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
5526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
5527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
5528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
5529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
5530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
5531bfbc30ff3b9e3a96b08c525d0971d8d8543ab000Marco Nelissen        URI_MATCHER.addURI("media", "*/audio/genres/all/members", AUDIO_GENRES_ALL_MEMBERS);
5532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
5533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
5534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
5535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
5536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
5537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
5538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
5539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
5540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
5541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
5542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
554371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
5544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
5546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
5547b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
5548b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
5549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
5551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
55520027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        URI_MATCHER.addURI("media", "*/fs_id", FS_ID);
5553704a8b507b7aa61a09457075ed6f80c95914d731Marco Nelissen        URI_MATCHER.addURI("media", "*/version", VERSION);
55540027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
5555819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood        URI_MATCHER.addURI("media", "*/mtp_connected", MTP_CONNECTED);
5556819cafdb3d4c3ce8a74d3b572b8ca0a0b639e8b2Mike Lockwood
5557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
5558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
5559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
5560b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood        // Used by MTP implementation
556116dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file", FILES);
556216dc0fdb9a80e09adb68864a7888c2ab6f3dc7afMike Lockwood        URI_MATCHER.addURI("media", "*/file/#", FILES_ID);
5563e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object", MTP_OBJECTS);
5564e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#", MTP_OBJECTS_ID);
5565e2c981f26abf2b46d0ff2175dc996fd680073b7bMike Lockwood        URI_MATCHER.addURI("media", "*/object/#/references", MTP_OBJECT_REFERENCES);
5566b78ad0d07a40f0d72dbe6c9ff365ddcfef316eb0Mike Lockwood
5567a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
5568a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
5569a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
5570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
5571a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
5572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
5573a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
5574a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
5575a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
5576a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
5577a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
5578a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
5579a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
5580a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
5581a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
5582a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
5583a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
5584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
558510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen
5586b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey    private static String getVolumeName(Uri uri) {
5587b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        final List<String> segments = uri.getPathSegments();
5588b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        if (segments != null && segments.size() > 0) {
5589b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey            return segments.get(0);
5590b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        } else {
5591b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey            return null;
5592b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey        }
5593b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey    }
5594b39b32d96e938d5a5792bb56e4c37f3e752493cdJeff Sharkey
559510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    @Override
559610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
559710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        Collection<DatabaseHelper> foo = mDatabases.values();
559810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        for (DatabaseHelper dbh: foo) {
5599988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            writer.println(dump(dbh, true));
5600988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        }
5601988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        writer.flush();
5602988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    }
5603988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen
5604988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen    private String dump(DatabaseHelper dbh, boolean dumpDbLog) {
5605988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        StringBuilder s = new StringBuilder();
5606988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        s.append(dbh.mName);
5607988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        s.append(": ");
5608988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        SQLiteDatabase db = dbh.getReadableDatabase();
5609988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        if (db == null) {
5610988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append("null");
5611988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        } else {
5612988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append("version " + db.getVersion() + ", ");
5613988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            Cursor c = db.query("files", new String[] {"count(*)"}, null, null, null, null, null);
5614988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            try {
5615988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (c != null && c.moveToFirst()) {
5616988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    int num = c.getInt(0);
5617988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    s.append(num + " rows, ");
5618988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                } else {
5619988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    s.append("couldn't get row count, ");
5620988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5621988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            } finally {
5622988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (c != null) {
5623988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    c.close();
5624988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5625988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            }
5626988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumInserts + " inserts, ");
5627988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumUpdates + " updates, ");
5628988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumDeletes + " deletes, ");
5629988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            s.append(dbh.mNumQueries + " queries, ");
5630988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            if (dbh.mScanStartTime != 0) {
5631988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                s.append("scan started " + DateUtils.formatDateTime(getContext(),
5632988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        dbh.mScanStartTime / 1000,
5633988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        DateUtils.FORMAT_SHOW_DATE
5634988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        | DateUtils.FORMAT_SHOW_TIME
5635988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        | DateUtils.FORMAT_ABBREV_ALL));
5636988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                long now = dbh.mScanStopTime;
5637988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (now < dbh.mScanStartTime) {
5638988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    now = SystemClock.currentTimeMicro();
5639988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5640988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                s.append(" (" + DateUtils.formatElapsedTime(
5641988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        (now - dbh.mScanStartTime) / 1000000) + ")");
5642988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                if (dbh.mScanStopTime < dbh.mScanStartTime) {
5643988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    if (mMediaScannerVolume != null &&
5644988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            dbh.mName.startsWith(mMediaScannerVolume)) {
5645988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        s.append(" (ongoing)");
564610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    } else {
5647988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        s.append(" (scanning " + mMediaScannerVolume + ")");
5648988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    }
5649988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                }
5650988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            }
5651988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen            if (dumpDbLog) {
5652988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                c = db.query("log", new String[] {"time", "message"},
5653f95e6b1acab8beb27a719aa368ae8d2525853befMarco Nelissen                        null, null, null, null, "rowid");
5654988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                try {
5655988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                    if (c != null) {
5656988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        while (c.moveToNext()) {
5657988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            String when = c.getString(0);
5658988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            String msg = c.getString(1);
5659988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                            s.append("\n" + when + " : " + msg);
5660988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen                        }
566110af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
566210af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                } finally {
566310af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    if (c != null) {
566410af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                        c.close();
566510af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                    }
566610af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen                }
566710af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen            }
566810af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen        }
5669988280a7b7cba5888b943a6db05aab703fd9c35aMarco Nelissen        return s.toString();
567010af34f31704509a71d02b0b4a15cfa07bbfede3Marco Nelissen    }
5671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
5672