MediaProvider.java revision 4d96d72ea42c2ec41a891f65623270473ae8eebd
1702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/*
2702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
3702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
4702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * you may not use this file except in compliance with the License.
6702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * You may obtain a copy of the License at
7702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
8702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project *
10702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * See the License for the specific language governing permissions and
14702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * limitations under the License.
15702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
16702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
17702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpackage com.android.providers.media;
18702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
19702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.app.SearchManager;
20702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.content.*;
21702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
22702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.SQLException;
23702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
24702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
25702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
26702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap;
27702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory;
28702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaFile;
29702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner;
30b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
31702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
32702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
33702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
34702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils;
35702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler;
36702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Looper;
3771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissenimport android.os.MemoryFile;
38702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message;
39702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor;
40702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process;
41702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns;
42702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore;
43702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio;
44702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images;
45702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns;
46702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video;
47702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images.ImageColumns;
48702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
51702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
52702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
54702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
55702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
56702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.text.Collator;
57702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
58702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
59702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
60b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
62702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
63702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/**
64702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details.
65702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the
66702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index).  The content visible at content://media/external/...
67702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card.
68702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
69702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider {
70702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri MEDIA_URI = Uri.parse("content://media");
71702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");
72b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int ALBUM_THUMB = 1;
73b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int IMAGE_THUMB = 2;
74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>();
76d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen    private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>();
77702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
84b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
85b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
86b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
87b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
88a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
89a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
90a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
91a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
92a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
93a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
94a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
95a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
96a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
97a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
98a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
99a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
100a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
101a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
102a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
103a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
104a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
105a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
106ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
107ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
108a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
111a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
112a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
113a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
114a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
115a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
116a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
11863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
121a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
122a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
123a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
124a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
125a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
126a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
127a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
12863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
12963f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
13063f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN text2!='" + MediaFile.UNKNOWN_STRING + "' THEN text2" +
13163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
132a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
133a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
13463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
13563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
136a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
137a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
138a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Remove the external volume and then notify all cursors backed by
144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // data on that volume
145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                detachVolume(Uri.parse("content://media/external"));
146d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                sFolderArtMap.clear();
147b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                MiniThumbFile.reset();
148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class DatabaseHelper extends SQLiteOpenHelper {
158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public DatabaseHelper(Context context, String name, boolean internal) {
166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            super(context, name, null, DATABASE_VERSION);
167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDatabase(db, mInternal, 0, DATABASE_VERSION);
177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDatabase(db, mInternal, oldV, newV);
186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Touch this particular database and garbage collect old databases.
190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums._ID);
261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.ALBUM_ART);
274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
27563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
27663f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
27763f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                        "%1", getContext().getString(R.string.artist_label));
278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().registerReceiver(mUnmountReceiver, iFilter);
284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
292b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        mThumbWorker = new Worker("thumbs thread");
293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mThumbHandler = new Handler(mThumbWorker.getLooper()) {
294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
296b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
297b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    MediaThumbRequest req;
298b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
299b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        req = mMediaThumbQueue.poll();
300b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
301b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    if (req == null) {
302b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
303b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
304b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        // Log.v(TAG, "we got work to do for checkThumbnail: "+ req.mPath );
305b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
3064d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            File origFile = new File(req.mPath);
3074d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            if (origFile.exists() && origFile.length() > 0) {
3084d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                req.execute();
3094d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            } else {
3104d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                // original file hasn't been stored yet
3114d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                synchronized (mMediaThumbQueue) {
3124d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                    Log.w(TAG, "original file hasn't been stored yet: " + req.mPath);
3134d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
3144d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
315b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
316b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            Log.e(TAG, "", ex);
317b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
318b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            req.mDone = true;
319b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            synchronized (req) {
320b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                req.notifyAll();
321b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
322b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
323b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
324b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
325b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
326b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
327b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
328b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
3298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
330b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    makeThumbInternal(d);
331b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
332b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
333b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
3348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDatabase(SQLiteDatabase db, boolean internal,
350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (toVersion != DATABASE_VERSION) {
354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " +
355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    DATABASE_VERSION);
356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
35895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 63) {
364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
419b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
47995ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(DISTINCT album) AS number_of_albums, " +
486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // For a given artist_id, provides the album_id for albums on
500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which the artist appears.
501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contiains mappings between audio genres and audio files
522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 66) {
618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateBucketNames(db, "images");
619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateBucketNames(db, "video");
632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
64295ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "artist='" + MediaFile.UNKNOWN_STRING + "' AND " +
653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album='" + MediaFile.UNKNOWN_STRING + "'" +
654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
65695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
67195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
6728d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
6738d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
6748d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
6758d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
6768d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
6778d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
6788d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
6798d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
6808d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
6818d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
6828d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
6838d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
6848d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
6858d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
6868d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
687a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
688a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
689a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
690a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
691a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
692a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
693a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
694a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
695a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
696a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
697a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
698a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
699a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
700a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
701a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
702a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
703a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
704a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
705a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
706a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
707a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
708a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
709a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaFile.UNKNOWN_STRING + "') " +
710a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
711a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
712a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
713a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
714a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
715a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
716a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
717a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
718a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
719a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
720a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
721a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
722a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
723a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM album_info WHERE (album!='" + MediaFile.UNKNOWN_STRING + "') " +
724a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
725a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
726a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
727a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
728a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
729a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
730a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
731a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
732a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
733a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
734a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
735a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
736a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
737a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
738a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
739a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
740a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
74159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
74259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
74395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
74459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
74559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
74659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
74759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
74815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
74915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
75015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
75115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
75215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
75315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
75415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
75515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
75615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
75715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
75815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
75915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
76015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
76115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
762b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
763b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
764b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
765b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
766b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
767b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
768b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
769b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
770b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
771b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
772b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
773b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
774b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
775b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
776b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
777b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
778b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
779b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
780b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note that views are read-only, so we define a trigger to allow deletes.
786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                "BEGIN " +
794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_meta where _id=old._id;" +
795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_playlists_map where audio_id=old._id;" +
796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_genres_map where audio_id=old._id;" +
797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                "END");
798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
79995ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the bucket_id and
802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * bucket_display_name columns are correct.
803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateBucketNames(SQLiteDatabase db, String tableName) {
807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ContentValues values = new ContentValues();
818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    computeBucketValues(data, values);
819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    int rowId = cursor.getInt(idColumnIndex);
820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    db.update(tableName, values, "_id=" + rowId, null);
821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_ID, path.hashCode());
888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
905b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
906b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
907b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
908b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
909b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
910b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
911b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
912b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
913b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
914b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c == null) return false;
915b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
916b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
917b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
918b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c.moveToFirst()) {
919b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String path = c.getString(1);
920b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            long magic = c.getLong(2);
921b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
922b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            if (magic == 0) {
9234d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                MediaThumbRequest req = requestMediaThumbnail(path, origUri,
9244d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                        MediaThumbRequest.PRIORITY_HIGH);
925b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                synchronized (req) {
926b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    try {
927b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        while (!req.mDone) {
928b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            req.wait();
929b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
930b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } catch (InterruptedException e) {
931b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, e);
932b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
933b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
934b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
935b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            result = true;
936b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
937b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        c.close();
938b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
939b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
940b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
941b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
942b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
943b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
944b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
945b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
946b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
947b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
948b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
949b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
950b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
951b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
952b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
953b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
954b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
955b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
956b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
957b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
958b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
959b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
960b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Uri origUri = Uri.parse("content://media" +
961b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                uri.getPath().replaceFirst("thumbnails", "media") + "/" + origId);
962b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
963b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
964b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Log.w(TAG, "original media doesn't exist.");
965b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
966b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
967b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
968b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
969b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
970b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
971b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
972b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
973b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
97901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return new MediaScannerCursor(mMediaScannerVolume);
987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getReadableDatabase();
996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
9974574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
998b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
1001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
1002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
1004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
1005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
1007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
1008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
1013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
1014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
1016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
1017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id = " + uri.getPathSegments().get(3));
1018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1019702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
1021b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
1022b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
1023b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
1024b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
1025b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio ");
1030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
1040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_genres_map WHERE audio_id = " +
1041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
1045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
1050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
1052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_playlists_map WHERE audio_id = " +
1053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
1057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
1062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
1066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1070702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
1071702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT audio_id FROM " +
1073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_genres_map WHERE genre_id = " +
1074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
1078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
1083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1087702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1089702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
109297e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
109397e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
109497e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        if (projectionIn[i].equals("_id")) {
109597e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
109697e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
1097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists_map, audio");
1100702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("audio._id = audio_id AND playlist_id = "
1101702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        + uri.getPathSegments().get(3));
1102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
1105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
1110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
1111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
1114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
1115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1118b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
1119b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
1120b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
1121b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
1122b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
1123b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1124b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1125b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1126702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
1127702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
1128702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
1131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
1132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
1136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
1137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
1138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " audio.album_id=album_art.album_id");
1139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
1140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artists_albums_map WHERE artist_id = " +
1141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                         aid + ")");
1142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                groupBy = "audio.album_id";
1143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
1144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
1145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
1146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
1147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
1150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
1151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
1154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
1155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
1159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
1160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + uri.getPathSegments().get(3));
1161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1163a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
1164a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
1165a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
1166a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
1167a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
1168a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection, selectionArgs, sort,
11694574e03055af60fada50481f2b34e19a687d5866Marco Nelissen                        table, limit);
1170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
1172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
1173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
11754d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection, selectionArgs, groupBy, null, sort, limit));
1176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
11774574e03055af60fada50481f2b34e19a687d5866Marco Nelissen                selectionArgs, groupBy, null, sort, limit);
1178b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
1180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            c.setNotificationUri(getContext().getContentResolver(), uri);
1181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1182b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
1184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
1187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
11884574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
11894574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
1190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1191a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String mSearchString = uri.toString().endsWith("/") ? "" : uri.getLastPathSegment();
1192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
1193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
1195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
1196a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
1197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Collator col = Collator.getInstance();
1198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        col.setStrength(Collator.PRIMARY);
1199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
1200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
1201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
1202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
1203a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
1204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
1205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        searchWords[i].equals("the")) ? "%" :
1206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                '%' + MediaStore.Audio.keyFor(searchWords[i]) + '%';
1207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1209a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
1210a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
1211a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
1212a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                where = "match LIKE ?";
1213a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
1214a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                where += " AND match LIKE ?";
1215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1218a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
1219a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
1220a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
1221a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
1222a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
1223a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
1224a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
1225a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
1226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
12274574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
1228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
1232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
1233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
1234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
1237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
1238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
1239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = query(url, MIME_TYPE_PROJECTION, null, null, null);
1240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (c != null && c.getCount() == 1) {
1241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    c.moveToFirst();
1242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String mimeType = c.getString(1);
1243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    c.deactivate();
1244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    return mimeType;
1245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
1249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
1250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
1251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
1252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
1253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
1256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
1257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
1258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
1260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
1262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
1263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
1264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
1265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
1266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
1267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
1268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
1270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
1271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
1273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
1274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        throw new IllegalStateException("Unknown URL");
1276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
1280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * present a new file is created.
1281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
1283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
1284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
1286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
1287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
1288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String file = initialValues.getAsString("_data");
1289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
1290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
1291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
1292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values.put("_data", file);
1293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
1295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!ensureFileExists(file)) {
1298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalStateException("Unable to create new file: " + file);
1299702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
1301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
1305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
1306702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
1307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
1308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
1310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
1311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
1312702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
1313702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1314702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getWritableDatabase();
1315702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1316702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
1317702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
1319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
1320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                insertInternal(uri, values[i]);
1321702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1322702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
1323702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1324702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
1328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
1329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Uri insert(Uri uri, ContentValues initialValues)
1333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
1334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = insertInternal(uri, initialValues);
1335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (newUri != null) {
1336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
1337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
1339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri insertInternal(Uri uri, ContentValues initialValues) {
1342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
1343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
1344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1345b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
1346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
1347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
1348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
1349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
1350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
1353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
1354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null && match != VOLUMES) {
1355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
1356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
1357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = (match == VOLUMES ? null : database.getWritableDatabase());
1359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (initialValues == null) {
1361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            initialValues = new ContentValues();
1362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
1365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
1366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg", "DCIM/Camera");
1367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
1369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String data = values.getAsString(MediaColumns.DATA);
1370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
1371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    computeDisplayName(data, values);
1372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeBucketValues(data, values);
1374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("images", "name", values);
1375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
1378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
13794d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                    requestMediaThumbnail(data, newUri, MediaThumbRequest.PRIORITY_NORMAL);
1380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1384b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
1385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
1386b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg",
1387b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
1388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
1389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
1391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContentUri(uri.getPathSegments().get(0)), rowId);
1392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1396b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
1397b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
1398b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg",
1399b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
1400b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
1401b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
1402b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
1403b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            getContentUri(uri.getPathSegments().get(0)), rowId);
1404b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1405b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1406b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
1407b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
1409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // SQLite Views are read-only, so we need to deconstruct this
1410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // insert and do inserts into the underlying tables.
1411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // If doing this here turns out to be a performance bottleneck,
1412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // consider moving this to native code and using triggers on
1413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // the view.
1414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Insert the artist into the artist table and remove it from
1417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // the input values
1418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Object so = values.get("artist");
1419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String s = (so == null ? "" : so.toString());
1420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.remove("artist");
1421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long artistRowId;
1422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashMap<String, Long> artistCache = database.mArtistCache;
142359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                String path = values.getAsString("_data");
1424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                synchronized(artistCache) {
1425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Long temp = artistCache.get(s);
1426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (temp == null) {
1427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        artistRowId = getKeyIdForName(db, "artists", "artist_key", "artist",
1428a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                s, s, path, 0, null, artistCache, uri);
1429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    } else {
1430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        artistRowId = temp.longValue();
1431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1433a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                String artist = s;
1434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Do the same for the album field
1436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                so = values.get("album");
1437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                s = (so == null ? "" : so.toString());
1438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.remove("album");
1439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long albumRowId;
1440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashMap<String, Long> albumCache = database.mAlbumCache;
1441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                synchronized(albumCache) {
144259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    int albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
144359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    String cacheName = s + albumhash;
144459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    Long temp = albumCache.get(cacheName);
1445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (temp == null) {
1446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        albumRowId = getKeyIdForName(db, "albums", "album_key", "album",
1447a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                s, cacheName, path, albumhash, artist, albumCache, uri);
1448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    } else {
1449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        albumRowId = temp;
1450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("artist_id", Integer.toString((int)artistRowId));
1454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("album_id", Integer.toString((int)albumRowId));
1455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                so = values.getAsString("title");
1456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                s = (so == null ? "" : so.toString());
1457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("title_key", MediaStore.Audio.keyFor(s));
1458358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                // do a final trim of the title, in case it started with the special
1459358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                // "sort first" character (ascii \001)
1460358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                values.remove("title");
1461358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                values.put("title", s.trim());
1462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeDisplayName(values.getAsString("_data"), values);
1464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
1465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_meta", "duration", values);
1467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
1469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
1474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
1475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
1477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "genre_id", values);
1478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
1485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
1486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
1488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
1489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
1490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
1497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres", "audio_id", initialValues);
1498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
1500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
1505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
1506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
1508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
1509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
1516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
1518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists", "name", initialValues);
1519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
1521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
1527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
1528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
1530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
1531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
1532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
1539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = ensureFile(database.mInternal, initialValues, ".3gp", "video");
1540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String data = values.getAsString("_data");
1541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeDisplayName(data, values);
1542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeBucketValues(data, values);
1543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
1544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("video", "artist", values);
1545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1546b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
1547b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            uri.getPathSegments().get(0)), rowId);
1548b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    requestMediaThumbnail(data, newUri, 0);
1549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART:
1554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database.mInternal) {
1555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
1556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = null;
1558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
1559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
1560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
1561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
1562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    values = initialValues;
1563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("album_art", "_data", values);
1565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
1571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return attachVolume(initialValues.getAsString("name"));
1572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
1574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
1575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
1578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
15804d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority) {
1581b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
158201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen            //Log.v(TAG, "requestMediaThumbnail: "+path+", "+uri+", magic="+magic);
1583b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest req = new MediaThumbRequest(
15844d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                    getContext().getContentResolver(), path, uri, priority);
1585b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            mMediaThumbQueue.add(req);
1586b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // Trigger the handler.
1587b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
1588b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            msg.sendToTarget();
1589b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
1590b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1591b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1592b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
1594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
1595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
1596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
1597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
1599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
1600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
1601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
1602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return Environment.getExternalStorageDirectory()
1604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                + "/" + directoryName + "/" + name + preferredExtension;
1605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private boolean ensureFileExists(String path) {
1609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
1610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
1611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
1612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
1614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
1615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
1616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
1617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
1618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
1619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
1620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
1621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.getParentFile().mkdirs();
1622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return file.createNewFile();
1624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
1625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
1626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
1628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
1632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
1633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
1634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
1637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
1638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
1640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
1641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
1642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
16439f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
16449f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                out.table = "images";
16459f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
16469f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
1647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "images";
1649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
1650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1652b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
1653b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
1654b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
1655b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
1656b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1657b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio";
1660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio";
1664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
1665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
1670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
1673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
1675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
1676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
1677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
1679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
1680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
1681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
1684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
1685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
1686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
1687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
1690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
1694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
1696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
1699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
1701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
1704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3) +
1706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND audio_id =" + uri.getPathSegments().get(5);
1707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
1710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
1711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
1715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
1716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
1719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
1720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
1721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
1724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
1725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
1726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
1727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
1730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
1731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
1732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
1735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "video";
1736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
1739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "video";
1740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
1741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1743b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
1744b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
1745b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
1746b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
1747b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1748b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
1750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
1751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
1752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
1755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
1756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
1757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
1758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
1759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
1760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
1763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
1768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
1769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
1770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
1772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
1773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
1774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
1775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1776702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
1777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
1778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match != VOLUMES_ID) {
1781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
1782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
1783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
1784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown URI: " + uri);
1785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
1787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
1789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
1790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
1791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    case AUDIO_MEDIA:
1792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    case AUDIO_MEDIA_ID:
1793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete("audio_meta",
1794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
1795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
1796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
1797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
1798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
1799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
1800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getContext().getContentResolver().notifyChange(uri, null);
1802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            detachVolume(uri);
1805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            count = 1;
1806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
1809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
1813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
1814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
1815b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
1816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
1817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
1818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
1819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
1820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
1821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getWritableDatabase();
1823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
1825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
1826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
1828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
1829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
1830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
1831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
1832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
1833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
1834a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
1835a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
1836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.remove("artist");
1837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
1838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            HashMap<String, Long> artistCache = database.mArtistCache;
1839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
1840a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
1841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
1842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = getKeyIdForName(db, "artists", "artist_key", "artist",
1843a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
1844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
1845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
1846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
1847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
1848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
1849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
1850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
185159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
1852a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
1853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
185459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            String path = values.getAsString("_data");
185559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
185659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            if (path == null) {
185759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                // If the path is null, we don't have a hash for the file in question.
185859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Log.w(TAG, "Update without specified path.");
185959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
186059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
186159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
1862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
1863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.remove("album");
1864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
1865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            HashMap<String, Long> albumCache = database.mAlbumCache;
1866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
186759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
186859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
1869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
1870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = getKeyIdForName(db, "albums", "album_key", "album",
1871a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
1872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
1873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
1874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
1875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
1876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
1877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
1878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
1880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
1881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
1882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
1883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
1884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
1885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
1886e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
1887e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
1888e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
1889e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
1890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
1891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update("audio_meta", values, sGetTableAndWhereParam.where,
1893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                whereArgs);
1894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
1896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
1897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
1898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
1899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
1900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
1901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
1902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
1903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
1904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
1905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
1906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
1907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
1908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
1909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
1910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
1911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
1912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
1913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
191401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
191501a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
191601a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
191701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
191801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
191901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
192001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
1921b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
1922b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                while (c.moveToNext()) {
192301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    long magic = c.getLong(2);
192401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    if (magic == 0) {
19254d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                        requestMediaThumbnail(c.getString(1), uri,
19264d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                                MediaThumbRequest.PRIORITY_NORMAL);
1927b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
1928b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
1929b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                c.close();
1930b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
1931b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
1932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
1934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
1935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
1936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
1937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
1938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (count > 0) {
1941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
1942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
1944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
1947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
1948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
1949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
195371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
1954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
195571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
195671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
195771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
195871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
195971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
196071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
196171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
196271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
196371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
196471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
196571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
196671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
196771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
196871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
196971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
197071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
197171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
197271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
197371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                String audiopath = c.getString(0);
197471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                int albumid = c.getInt(1);
197571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // Try to get existing album art for this album first, which
197671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // could possibly have been obtained from a different file.
197771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // If that fails, try to get it from this specific file.
197871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
197971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
198071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = openFile(newUri, mode);  // recursive call
198171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
198271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    // That didn't work, now try to get it from the specific file
198371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, null);
198471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
198571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
198671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            c.close();
198771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
198871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
198971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
1990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd = openFileHelper(uri, mode);
1992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
199371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
199471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
199571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
199671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
199771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
1998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
1999702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
2000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
2001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
2002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
2003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
2005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
2006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
200771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
2008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
2009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
2010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
2011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
2012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        null, null, null, null, null);
201371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                if (c.moveToFirst()) {
2014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String audiopath = c.getString(0);
201571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, uri);
2016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                c.close();
2018702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
201971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
202071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
202171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
2022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
2024702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class Worker implements Runnable {
2027702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        private final Object mLock = new Object();
2028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        private Looper mLooper;
2029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Worker(String name) {
2031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Thread t = new Thread(null, this, name);
2032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            t.start();
2033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (mLock) {
2034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (mLooper == null) {
2035702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    try {
2036702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mLock.wait();
2037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    } catch (InterruptedException ex) {
2038702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2039702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2040702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2041702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2042702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2043702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public Looper getLooper() {
2044702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return mLooper;
2045702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void run() {
2048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (mLock) {
2049f75dba4941649c19bfe9c14a6ef152af21346056Marco Nelissen                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
2050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Looper.prepare();
2051702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mLooper = Looper.myLooper();
2052702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mLock.notifyAll();
2053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Looper.loop();
2055702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void quit() {
2058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mLooper.quit();
2059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
2063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
2064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
2065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
2066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
2067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2069a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private void makeThumbAsync(SQLiteDatabase db, String path, long album_id) {
20708a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
20718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
20728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
20738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
20748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
20758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
20768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
20778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
20788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
20798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
2080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
2081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
2082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
2083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
2084a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
20858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
20868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
20878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
20888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
20898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
20908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
20918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
20928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
20938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
20948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
20958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
2096b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
2097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
2098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
21008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
21018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
21028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
21038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
2104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
2107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
2108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
2109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
21108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
21118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
2112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
2113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2114d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
2115d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // same directory as the media file.
2116d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
2117d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
2118d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
2119d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
2120d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
2121d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
21228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
2123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
2124702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
2125d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
2126d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String artPath = path.substring(0, lastSlash + 1);
2127d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
2128d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
2129d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
2130d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
2131d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
2132d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        } else {
2133d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
2134d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
2135d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
2136d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
2137d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
2138d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
2139d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
2140d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
2141d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
2142d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
2143d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2144d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
2145d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
2146d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
2147d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
2148d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2149d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
2150d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
2151d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
2152d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
2153d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2154d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
2155d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
2156d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2157d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
2158d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
2159d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2160d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
2161d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
2162d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
2163d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
2164d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
2165d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
2166d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
2167d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
2168d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
2169d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        File file = new File(artPath  + bestmatch);
2170d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
2171d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            compressed = new byte[(int)file.length()];
2172d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
2173d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
2174d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
2175d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
2176d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
2177d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
2178d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
2179d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
2180d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
2181d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
2182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
2183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
21878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
21888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
2189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
21908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
21918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
2192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
21938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
21948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    Uri getAlbumArtOutputUri(SQLiteDatabase db, long album_id, Uri albumart_uri) {
21958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
21968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
21978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
21988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
21998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Cursor c = query(albumart_uri, new String [] { "_data" },
22008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
220171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
22028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                String albumart_path = c.getString(0);
22038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (ensureFileExists(albumart_path)) {
22048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = albumart_uri;
2205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
220671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } else {
220771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                albumart_uri = null;
2208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
22098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            c.close();
221071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
221171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
22128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
22138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
22148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
22158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
22168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                long rowId = db.insert("album_art", "_data", values);
22178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
22188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
2219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
22208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
22218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
22228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
22238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
22248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
22258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
22268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
22278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
22288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
22298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
22308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) {
22318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean success = false;
22328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
22338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            OutputStream outstream = getContext().getContentResolver().openOutputStream(out);
22348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
22358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
22368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
22378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
22388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
22398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = true;
22408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
22418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = bm.compress(Bitmap.CompressFormat.JPEG, 75, outstream);
2242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
22438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
22448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            outstream.close();
22458a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (FileNotFoundException ex) {
22468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
2247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (IOException ex) {
22488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
22498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
22508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (!success) {
22518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // the thumbnail was not written successfully, delete the entry that refers to it
22528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            getContext().getContentResolver().delete(out, null, null);
2253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
22548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
22558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
225671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor getThumb(SQLiteDatabase db, String path, long album_id,
225771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Uri albumart_uri) {
225871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
225971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
226071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
226171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
226271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
226371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
226471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
226571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
226671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
22678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
2268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
22698a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
227071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
22718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
22728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
22738a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
22748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
22758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
22768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
22778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
22788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
22798a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
22808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
22818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
22828a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
22838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
22848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // TODO: don't hardcode the size
22858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            while (opts.outHeight > 320 || opts.outWidth > 320) {
22868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
22878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
22888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
22898a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
22908a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
22918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
22928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
22938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
22948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
22958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
22968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
22978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
22988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
22998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
23008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
23018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
23028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    bm = bm.copy(Bitmap.Config.RGB_565, false);
23038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
23048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
23058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
23068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
23078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
23088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
230971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
23108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
23118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
231271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
231371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
231471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
231571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
231671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                MemoryFile file = new MemoryFile("albumthumb", compressed.length);
231771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                file.writeBytes(compressed, 0, 0, compressed.length);
231871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                file.deactivate();
231971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                return file.getParcelFileDescriptor();
232071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
232171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
232271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
232371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one needs to actually be saved on the sd card
232471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Uri out = getAlbumArtOutputUri(d.db, d.album_id, d.albumart_uri);
23258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
232671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (out != null) {
232771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                writeAlbumArt(need_to_recompress, out, compressed, bm);
232871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                getContext().getContentResolver().notifyChange(MEDIA_URI, null);
232971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
233071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    return openFileHelper(out, "r");
233171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
233271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
233371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
23348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
233571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
2336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
2340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
2341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
2342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
2343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
2344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
2345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
234659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
234759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
234859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
2349a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
2350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
2351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
2352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
2353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
2354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private long getKeyIdForName(SQLiteDatabase db, String table, String keyField, String nameField,
235659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
2357a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
2358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
2359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
2361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
2362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
2364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
2366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
2367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
236959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
237059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isUnknown = MediaFile.UNKNOWN_STRING.equals(rawName);
237159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
237259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // To distinguish same-named albums, we append a hash of the path.
237359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
237459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
237559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
237659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
237759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
2378a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
237959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
2380a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
2381a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
2382a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
238359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
238459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
2385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
2386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
2387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
2390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
2391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
2392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
2393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
2394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
2395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
239659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
2397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
2398a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen                            makeThumbAsync(db, path, rowId);
2399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
2401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
2402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
2403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
2404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
2408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
2409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
2410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
2411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
2413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
2414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
2415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
2416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
2417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
2418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
2419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
2420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
2421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
2422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
2423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
2424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
2428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
2429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
2430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
2431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (c != null) c.close();
2435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
243759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
243859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
2439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
2441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
2445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
2446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
2447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
2448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
2449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
2451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
2452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
2453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
2454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
2456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
2457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
2458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
2460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
2461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
2463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
2464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
2465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
2467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
2468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
2469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
2471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
2475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
2476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
2477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
2478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
2479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
2480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
2483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
2484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
2489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
2491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
2492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
2494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
2495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (uri.getPathSegments().size() > 1) {
2496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
2497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
2500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
2504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
2505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
2506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
2508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
2509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
2511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) {
2512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
2513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
2514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
2517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
2518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
2519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper db;
2522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
2523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db = new DatabaseHelper(getContext(), INTERNAL_DATABASE_NAME, true);
2524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
2525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String path = Environment.getExternalStorageDirectory().getPath();
2526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int volumeID = FileUtils.getFatVolumeId(path);
2527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
2528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // generate database name based on volume ID
2530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
2531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db = new DatabaseHelper(getContext(), dbName, false);
2532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
2534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.put(volume, db);
2537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!db.mInternal) {
2539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
2540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File[] files = new File(
2541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        Environment.getExternalStorageDirectory(),
2542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ALBUM_THUMB_FOLDER).listFiles();
2543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
2544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
2545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
2546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
2549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
2550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
2551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
2552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
2553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
2555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (cursor != null) cursor.close();
2556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
2559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
2560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
2561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
2562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
2563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
2568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
2569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
2573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
2574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
2575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
2577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
2579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) {
2580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
2581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
2582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
2585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
2586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
2588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
2589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
2590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
2591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
2594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
2595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
2596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
2598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
2599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
2600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
2601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch (SQLException e) {
2602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
2603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
2606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
2607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
2610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
2611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
2614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final boolean LOCAL_LOGV = true;
2615b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int DATABASE_VERSION = 77;
2616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
2617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
2619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
2620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
2622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
2623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
2624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
2626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Worker mThumbWorker;
2628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
2631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
2632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
2634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
2635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String ALBUM_THUMB_FOLDER = "albumthumbs";
2636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
2638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
2639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
2641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
2642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
2643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
2644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
2646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
2647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
2648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
2649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
2650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
2651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
2652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
2653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
2654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS_ID = 109;
2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
2656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
2657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
2658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
2659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
2660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
2661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
2662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
2663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
2664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
2665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
266671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
2667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
2669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
2670b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
2671b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
2672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
2674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
2675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2676a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
2677a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
2678a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
2679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
2681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
2683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
2684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2685b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
2686b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
2687b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
2688b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
2690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
2691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
2692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
2693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2694b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
2695b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
2696b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
2697b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
2698b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
2699b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] EXTERNAL_DATABASE_TABLES = new String[] {
2701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "images",
2702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "thumbnails",
2703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_meta",
2704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "artists",
2705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "albums",
2706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_genres",
2707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_genres_map",
2708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_playlists",
2709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_playlists_map",
2710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "video",
2711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
2712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
2714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
2717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
2718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
2719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
2721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
2722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
2726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
2727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
2728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
2729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members/#", AUDIO_GENRES_ID_MEMBERS_ID);
2730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
2731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
2732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
2733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
2734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
2735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
2736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
2737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
2738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
2739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
2740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
274171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
2742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
2744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
2745b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
2746b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
2747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
2749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
2751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
2752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2753a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
2754a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
2755a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
2756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
2757a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
2758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
2759a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
2760a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
2761a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
2762a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
2763a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
2764a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
2765a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
2766a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
2767a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
2768a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
2769a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
2770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
2772