MediaProvider.java revision ab43e1853533e4d9352c251d53c36fb645077e43
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.*;
210027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.AbstractCursor;
22702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.Cursor;
23ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissenimport android.database.DatabaseUtils;
240027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissenimport android.database.MatrixCursor;
25702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.SQLException;
26702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteDatabase;
27702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteOpenHelper;
28702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.database.sqlite.SQLiteQueryBuilder;
29702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.Bitmap;
30702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.graphics.BitmapFactory;
31702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.media.MediaScanner;
32b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport android.media.MiniThumbFile;
33702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.net.Uri;
34702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Binder;
35702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Environment;
36702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.FileUtils;
37702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Handler;
38ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Changimport android.os.HandlerThread;
39702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Looper;
4071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissenimport android.os.MemoryFile;
41702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Message;
42702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.ParcelFileDescriptor;
43702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.os.Process;
44702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.BaseColumns;
45702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore;
46702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Audio;
47702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images;
48702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.MediaColumns;
49702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Video;
50702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.provider.MediaStore.Images.ImageColumns;
51702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.text.TextUtils;
52702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport android.util.Log;
53702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
54702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.File;
55702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileInputStream;
56702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.FileNotFoundException;
57702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.IOException;
58702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.io.OutputStream;
59702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.text.Collator;
60cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissenimport java.util.ArrayList;
61702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashMap;
62702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.HashSet;
63702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectimport java.util.Iterator;
64f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissenimport java.util.List;
65b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chenimport java.util.PriorityQueue;
668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huberimport java.util.Stack;
67702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
68702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project/**
69702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Media content provider. See {@link android.provider.MediaStore} for details.
70702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * Separate databases are kept for each external storage card we see (using the
71702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * card's ID as an index).  The content visible at content://media/external/...
72702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project * changes with the card.
73702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project */
74702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Projectpublic class MediaProvider extends ContentProvider {
75702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri MEDIA_URI = Uri.parse("content://media");
76702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final Uri ALBUMART_URI = Uri.parse("content://media/external/audio/albumart");
77b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int ALBUM_THUMB = 1;
78b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int IMAGE_THUMB = 2;
79702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
80702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final HashMap<String, String> sArtistAlbumsMap = new HashMap<String, String>();
81d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen    private static final HashMap<String, String> sFolderArtMap = new HashMap<String, String>();
82702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
838a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A HashSet of paths that are pending creation of album art thumbnails.
848a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private HashSet mPendingThumbs = new HashSet();
858a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
868a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // A Stack of outstanding thumbnail requests.
878a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private Stack mThumbRequestStack = new Stack();
888a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
8920434e032e498b716f87cce2f23dd646819218bfRay Chen    // The lock of mMediaThumbQueue protects both mMediaThumbQueue and mCurrentThumbRequest.
9020434e032e498b716f87cce2f23dd646819218bfRay Chen    private MediaThumbRequest mCurrentThumbRequest = null;
91b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private PriorityQueue<MediaThumbRequest> mMediaThumbQueue =
92b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            new PriorityQueue<MediaThumbRequest>(MediaThumbRequest.PRIORITY_NORMAL,
93b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaThumbRequest.getComparator());
94b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
95a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // For compatibility with the approximately 0 apps that used mediaprovider search in
96a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    // releases 1.0, 1.1 or 1.5
97a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsLegacy = new String[] {
98a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
99a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
100a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
101a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
102a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
103a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
104a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "0 AS " + SearchManager.SUGGEST_COLUMN_ICON_2,
105a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
106a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
107a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data1 ELSE artist END AS data1",
108a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "CASE when grouporder=1 THEN data2 ELSE " +
109a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "CASE WHEN grouporder=2 THEN NULL ELSE album END END AS data2",
110a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "match as ar",
111a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA,
112a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "grouporder",
113ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen            "NULL AS itemorder" // We should be sorting by the artist/album/title keys, but that
114ea74c8add2d5b4215dfeb69183632d9e9797ac5aMarco Nelissen                                // column is not available here, and the list is already sorted.
115a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
116a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsFancy = new String[] {
117a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
118a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
119a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Artists.ARTIST,
120a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Albums.ALBUM,
121a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.TITLE,
122a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data1",
123a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "data2",
124a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
12563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // If this array gets changed, please update the constant below to point to the correct item.
126a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private String[] mSearchColsBasic = new String[] {
127a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            android.provider.BaseColumns._ID,
128a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            MediaStore.Audio.Media.MIME_TYPE,
129a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "(CASE WHEN grouporder=1 THEN " + R.drawable.ic_search_category_music_artist +
130a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE CASE WHEN grouporder=2 THEN " + R.drawable.ic_search_category_music_album +
131a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            " ELSE " + R.drawable.ic_search_category_music_song + " END END" +
132a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            ") AS " + SearchManager.SUGGEST_COLUMN_ICON_1,
133a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1,
134a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            "text1 AS " + SearchManager.SUGGEST_COLUMN_QUERY,
13563f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            "(CASE WHEN grouporder=1 THEN '%1'" +  // %1 gets replaced with localized string.
13663f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE CASE WHEN grouporder=3 THEN artist || ' - ' || album" +
137e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen            " ELSE CASE WHEN text2!='" + MediaStore.UNKNOWN_STRING + "' THEN text2" +
13863f748ff8b258d9110038778a006b3000164fbeeSatish Sampath            " ELSE NULL END END END) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2,
139a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            SearchManager.SUGGEST_COLUMN_INTENT_DATA
140a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    };
14163f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    // Position of the TEXT_2 item in the above array.
14263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath    private final int SEARCH_COLUMN_BASIC_TEXT2 = 5;
143a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
144a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private Uri mAlbumArtBaseUri = Uri.parse("content://media/external/audio/albumart");
145a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen
146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onReceive(Context context, Intent intent) {
149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Remove the external volume and then notify all cursors backed by
151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // data on that volume
152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                detachVolume(Uri.parse("content://media/external"));
153d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                sFolderArtMap.clear();
154b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                MiniThumbFile.reset();
155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Wrapper class for a specific database (associated with one particular
161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * external card, or with internal storage).  Can open the actual database
162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * on demand, create and upgrade the schema, etc.
163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class DatabaseHelper extends SQLiteOpenHelper {
165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final Context mContext;
166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        final boolean mInternal;  // True if this is the internal database
167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // In memory caches of artist and album data.
169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mArtistCache = new HashMap<String, Long>();
170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        HashMap<String, Long> mAlbumCache = new HashMap<String, Long>();
171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public DatabaseHelper(Context context, String name, boolean internal) {
173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            super(context, name, null, DATABASE_VERSION);
174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mContext = context;
175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mInternal = internal;
176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Creates database the first time we try to open it.
180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onCreate(final SQLiteDatabase db) {
183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDatabase(db, mInternal, 0, DATABASE_VERSION);
184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Updates the database format when a new content provider is used
188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * with an older database format.
189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onUpgrade(final SQLiteDatabase db, final int oldV, final int newV) {
192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDatabase(db, mInternal, oldV, newV);
193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
195702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        /**
196702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * Touch this particular database and garbage collect old databases.
197702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * An LRU cache system is used to clean up databases for old external
198702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         * storage volumes.
199702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project         */
200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        @Override
201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public void onOpen(SQLiteDatabase db) {
202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mInternal) return;  // The internal database is kept separately.
203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // touch the database file to show it is most recently used
205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File file = new File(db.getPath());
206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long now = System.currentTimeMillis();
207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.setLastModified(now);
208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases if we are over the limit
210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] databases = mContext.databaseList();
211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int count = databases.length;
212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int limit = MAX_EXTERNAL_DATABASES;
213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete external databases that have not been used in the past two months
215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            long twoMonthsAgo = now - OBSOLETE_DATABASE_DB;
216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < databases.length; i++) {
217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File other = mContext.getDatabasePath(databases[i]);
218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (INTERNAL_DATABASE_NAME.equals(databases[i]) || file.equals(other)) {
219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[i] = null;
220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
221702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (file.equals(other)) {
222702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // reduce limit to account for the existence of the database we
223702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // are about to open, which we removed from the list.
224702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        limit--;
225702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
226702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } else {
227702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    long time = other.lastModified();
228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (time < twoMonthsAgo) {
229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[i]);
230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        mContext.deleteDatabase(databases[i]);
231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        databases[i] = null;
232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count--;
233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // delete least recently used databases until
238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we are no longer over the limit
239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            while (count > limit) {
240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lruIndex = -1;
241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long lruTime = 0;
242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; i < databases.length; i++) {
244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (databases[i] != null) {
245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        long time = mContext.getDatabasePath(databases[i]).lastModified();
246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (lruTime == 0 || time < lruTime) {
247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruIndex = i;
248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            lruTime = time;
249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // delete least recently used database
254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lruIndex != -1) {
255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "Deleting old database " + databases[lruIndex]);
256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    mContext.deleteDatabase(databases[lruIndex]);
257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    databases[lruIndex] = null;
258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count--;
259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public boolean onCreate() {
266702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums._ID, "audio.album_id AS " +
267702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums._ID);
268702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM, "album");
269702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_KEY, "album_key");
270702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.FIRST_YEAR, "MIN(year) AS " +
271702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.FIRST_YEAR);
272702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.LAST_YEAR, "MAX(year) AS " +
273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.LAST_YEAR);
274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST, "artist");
275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_ID, "artist");
276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Media.ARTIST_KEY, "artist_key");
277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS, "count(*) AS " +
278702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.NUMBER_OF_SONGS);
279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        sArtistAlbumsMap.put(MediaStore.Audio.Albums.ALBUM_ART, "album_art._data AS " +
280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                MediaStore.Audio.Albums.ALBUM_ART);
281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
28263f748ff8b258d9110038778a006b3000164fbeeSatish Sampath        mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2] =
28363f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                mSearchColsBasic[SEARCH_COLUMN_BASIC_TEXT2].replaceAll(
28463f748ff8b258d9110038778a006b3000164fbeeSatish Sampath                        "%1", getContext().getString(R.string.artist_label));
285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mDatabases = new HashMap<String, DatabaseHelper>();
286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        attachVolume(INTERNAL_VOLUME);
287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        IntentFilter iFilter = new IntentFilter(Intent.ACTION_MEDIA_EJECT);
289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        iFilter.addDataScheme("file");
290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().registerReceiver(mUnmountReceiver, iFilter);
291702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
292702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // open external database if external storage is mounted
293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String state = Environment.getExternalStorageState();
294702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Environment.MEDIA_MOUNTED.equals(state) ||
295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            attachVolume(EXTERNAL_VOLUME);
297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
299ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        HandlerThread ht = new HandlerThread("thumbs thread", Process.THREAD_PRIORITY_BACKGROUND);
300ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        ht.start();
301ab43e1853533e4d9352c251d53c36fb645077e43Chih-Chung Chang        mThumbHandler = new Handler(ht.getLooper()) {
302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            @Override
303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            public void handleMessage(Message msg) {
304b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (msg.what == IMAGE_THUMB) {
305b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mMediaThumbQueue) {
30620434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest = mMediaThumbQueue.poll();
307b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
30820434e032e498b716f87cce2f23dd646819218bfRay Chen                    if (mCurrentThumbRequest == null) {
309b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        Log.w(TAG, "Have message but no request?");
310b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    } else {
311b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        try {
31220434e032e498b716f87cce2f23dd646819218bfRay Chen                            File origFile = new File(mCurrentThumbRequest.mPath);
3134d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            if (origFile.exists() && origFile.length() > 0) {
31420434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.execute();
3154d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            } else {
3164d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                // original file hasn't been stored yet
3174d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                synchronized (mMediaThumbQueue) {
31820434e032e498b716f87cce2f23dd646819218bfRay Chen                                    Log.w(TAG, "original file hasn't been stored yet: " + mCurrentThumbRequest.mPath);
3194d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                }
3204d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                            }
321b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } catch (IOException ex) {
3221d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
3231d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                        } catch (UnsupportedOperationException ex) {
3241d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // This could happen if we unplug the sd card during insert/update/delete
3251d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            // See getDatabaseForUri.
3261d6eba9e1e28c722aa73a651d86a2efe2b937bf2Ray Chen                            Log.w(TAG, ex);
327b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        } finally {
32820434e032e498b716f87cce2f23dd646819218bfRay Chen                            synchronized (mCurrentThumbRequest) {
32920434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.mState = MediaThumbRequest.State.DONE;
33020434e032e498b716f87cce2f23dd646819218bfRay Chen                                mCurrentThumbRequest.notifyAll();
331b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
332b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
333b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
334b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                } else if (msg.what == ALBUM_THUMB) {
335b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ThumbData d;
336b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mThumbRequestStack) {
337b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        d = (ThumbData)mThumbRequestStack.pop();
338b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
3398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
340b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    makeThumbInternal(d);
341b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    synchronized (mPendingThumbs) {
342b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        mPendingThumbs.remove(d.path);
343b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    }
3448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        };
347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return true;
349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method takes care of updating all the tables in the database to the
353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * current version, creating them if necessary.
354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * This method can only update databases at schema 63 or higher, which was
355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * created August 1, 2008. Older database will be cleared and recreated.
356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db Database
357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param internal True if this is the internal media database
358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDatabase(SQLiteDatabase db, boolean internal,
360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int fromVersion, int toVersion) {
361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // sanity checks
363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (toVersion != DATABASE_VERSION) {
364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.e(TAG, "Illegal update request. Got " + toVersion + ", expected " +
365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    DATABASE_VERSION);
366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (fromVersion > toVersion) {
36895ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            Log.e(TAG, "Illegal update request: can't downgrade from " + fromVersion +
369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " to " + toVersion + ". Did you forget to wipe data?");
370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException();
371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 63) {
374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Drop everything and start over.
375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Log.i(TAG, "Upgrading media database from version " +
376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fromVersion + " to " + toVersion + ", which will destroy all old data");
377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS images");
378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS images_cleanup");
379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS thumbnails");
380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS thumbnails_cleanup");
381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_meta");
382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS artists");
383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS albums");
384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS album_art");
385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artist_info");
386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS album_info");
387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP VIEW IF EXISTS artists_albums_map");
388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_meta_cleanup");
389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres");
390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_genres_map");
391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_genres_cleanup");
392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists");
393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS audio_playlists_map");
394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS audio_playlists_cleanup");
395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup1");
396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS albumart_cleanup2");
397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TABLE IF EXISTS video");
398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("DROP TRIGGER IF EXISTS video_cleanup");
399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS images (" +
401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_id INTEGER PRIMARY KEY," +
402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT," +
403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_size INTEGER," +
404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_display_name TEXT," +
405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mime_type TEXT," +
406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "title TEXT," +
407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_added INTEGER," +
408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "date_modified INTEGER," +
409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "description TEXT," +
410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "picasa_id TEXT," +
411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "isprivate INTEGER," +
412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "latitude DOUBLE," +
413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "longitude DOUBLE," +
414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "datetaken INTEGER," +
415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "orientation INTEGER," +
416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "mini_thumb_magic INTEGER," +
417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_id TEXT," +
418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "bucket_display_name TEXT" +
419702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
420702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
421702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS mini_thumb_magic_index on images(mini_thumb_magic);");
422702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS images_cleanup DELETE ON images " +
424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "DELETE FROM thumbnails WHERE image_id = old._id;" +
426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
429b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create image thumbnail table
430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS thumbnails (" +
431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT," +
433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "image_id INTEGER," +
434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "kind INTEGER," +
435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "width INTEGER," +
436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "height INTEGER" +
437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS image_id_index on thumbnails(image_id);");
440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS thumbnails_cleanup DELETE ON thumbnails " +
442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about audio files
447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS audio_meta (" +
448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
449702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
450702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
451702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
452702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
453702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
454702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
455702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT NOT NULL," +
456702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title_key TEXT NOT NULL," +
457702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
458702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist_id INTEGER," +
459702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "composer TEXT," +
460702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album_id INTEGER," +
461702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "track INTEGER," +    // track is an integer to allow proper sorting
462702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "year INTEGER CHECK(year!=0)," +
463702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_ringtone INTEGER," +
464702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_music INTEGER," +
465702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_alarm INTEGER," +
466702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "is_notification INTEGER" +
467702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
469702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for artists
470702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS artists (" +
471702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_id INTEGER PRIMARY KEY," +
472702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist_key TEXT NOT NULL UNIQUE," +
473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artist TEXT NOT NULL" +
474702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
475702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
476702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains a sort/group "key" and the preferred display name for albums
477702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS albums (" +
478702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_id INTEGER PRIMARY KEY," +
479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album_key TEXT NOT NULL UNIQUE," +
480702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "album TEXT NOT NULL" +
481702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
482702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
483702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS album_art (" +
484702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "album_id INTEGER PRIMARY KEY," +
485702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "_data TEXT" +
486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                   ");");
487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
48995ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
490702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides some extra info about artists, like the number of tracks
492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and albums for this artist
493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artist_info AS " +
494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT artist_id AS _id, artist, artist_key, " +
495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(DISTINCT album) AS number_of_albums, " +
496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "COUNT(*) AS number_of_tracks FROM audio WHERE is_music=1 "+
497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "GROUP BY artist_key;");
498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Provides extra info albums, such as the number of tracks
500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS album_info AS " +
501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT audio.album_id AS _id, album, album_key, " +
502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MIN(year) AS minyear, " +
503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "MAX(year) AS maxyear, artist, artist_id, artist_key, " +
504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "count(*) AS " + MediaStore.Audio.Albums.NUMBER_OF_SONGS +
505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ",album_art._data AS album_art" +
506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " FROM audio LEFT OUTER JOIN album_art ON audio.album_id=album_art.album_id" +
507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " WHERE is_music=1 GROUP BY audio.album_id;");
508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // For a given artist_id, provides the album_id for albums on
510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which the artist appears.
511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE VIEW IF NOT EXISTS artists_albums_map AS " +
512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT DISTINCT artist_id, album_id FROM audio_meta;");
513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            /*
515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             * Only external media volumes can handle genres, playlists, etc.
516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project             */
517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!internal) {
518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio file is deleted
519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_meta_cleanup DELETE ON audio_meta " +
520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE audio_id = old._id;" +
522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE audio_id = old._id;" +
523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
524702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio genre definitions
526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres (" +
527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL" +
529702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
531702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contiains mappings between audio genres and audio files
532702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_genres_map (" +
533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "genre_id INTEGER NOT NULL" +
536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio genre is delete
539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_genres_cleanup DELETE ON audio_genres " +
540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
541702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_genres_map WHERE genre_id = old._id;" +
542702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
543702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
544702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains audio playlist definitions
545702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists (" +
546702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
547702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_data TEXT," +  // _data is path for file based playlists, or null
548702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "name TEXT NOT NULL," +
549702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_added INTEGER," +
550702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "date_modified INTEGER" +
551702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
552702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Contains mappings between audio playlists and audio files
554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TABLE IF NOT EXISTS audio_playlists_map (" +
555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "_id INTEGER PRIMARY KEY," +
556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "audio_id INTEGER NOT NULL," +
557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "playlist_id INTEGER NOT NULL," +
558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "play_order INTEGER NOT NULL" +
559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           ");");
560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up when an audio playlist is deleted
562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_playlists_cleanup DELETE ON audio_playlists " +
563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "BEGIN " +
564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "DELETE FROM audio_playlists_map WHERE playlist_id = old._id;" +
565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                               "SELECT _DELETE_FILE(old._data);" +
566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                           "END");
567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
568702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art table entry when an album is deleted
569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup1 DELETE ON albums " +
570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "DELETE FROM album_art WHERE album_id = old.album_id;" +
572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
573702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Cleans up album_art when an album is deleted
575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db.execSQL("CREATE TRIGGER IF NOT EXISTS albumart_cleanup2 DELETE ON album_art " +
576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "BEGIN " +
577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            "SELECT _DELETE_FILE(old._data);" +
578702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "END");
579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Contains meta data about video files
582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TABLE IF NOT EXISTS video (" +
583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_id INTEGER PRIMARY KEY," +
584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_data TEXT NOT NULL," +
585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_display_name TEXT," +
586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "_size INTEGER," +
587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mime_type TEXT," +
588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_added INTEGER," +
589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "date_modified INTEGER," +
590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "title TEXT," +
591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "duration INTEGER," +
592702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "artist TEXT," +
593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "album TEXT," +
594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "resolution TEXT," +
595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "description TEXT," +
596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "isprivate INTEGER," +   // for YouTube videos
597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "tags TEXT," +           // for YouTube videos
598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "category TEXT," +       // for YouTube videos
599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "language TEXT," +       // for YouTube videos
600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_data TEXT," +
601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "latitude DOUBLE," +
602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "longitude DOUBLE," +
603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "datetaken INTEGER," +
604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       "mini_thumb_magic INTEGER" +
605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                       ");");
606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE TRIGGER IF NOT EXISTS video_cleanup DELETE ON video " +
608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "BEGIN " +
609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "SELECT _DELETE_FILE(old._data);" +
610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "END");
611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // At this point the database is at least at schema version 63 (it was
614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // either created at version 63 by the code above, or was already at
615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // version 63 or later)
616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 64) {
618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 64
619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS sort_index on images(datetaken ASC, _id ASC);");
620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
622702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 65) {
623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the index that updates the database to schema version 65
624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS titlekey_index on audio_meta(title_key);");
625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 66) {
628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateBucketNames(db, "images");
629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 67) {
632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // create the indices that update the database to schema version 67
633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS albumkey_index on albums(album_key);");
634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("CREATE INDEX IF NOT EXISTS artistkey_index on artists(artist_key);");
635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 68) {
638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bucket_id and bucket_display_name columns for the video table.
639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_id TEXT;");
640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bucket_display_name TEXT");
641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateBucketNames(db, "video");
642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 69) {
645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            updateDisplayName(db, "images");
646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 70) {
649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create bookmark column for the video table.
650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE video ADD COLUMN bookmark INTEGER;");
651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
65295ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 71) {
654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // There is no change to the database schema, however a code change
655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // fixed parsing of metadata for certain files bought from the
656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // iTunes music store, so we want to rescan files that might need it.
657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // We do this by clearing the modification date in the database for
658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // those files, so that the media scanner will see them as updated
659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // and rescan them.
660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET date_modified=0 WHERE _id IN (" +
661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "SELECT _id FROM audio where mime_type='audio/mp4' AND " +
662e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "artist='" + MediaStore.UNKNOWN_STRING + "' AND " +
663e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "album='" + MediaStore.UNKNOWN_STRING + "'" +
664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ");");
665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
66695ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (fromVersion < 72) {
668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Create is_podcast and bookmark columns for the audio table.
669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN is_podcast INTEGER;");
670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE _data LIKE '%/podcasts/%';");
671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("UPDATE audio_meta SET is_music=0 WHERE is_podcast=1" +
672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    " AND _data NOT LIKE '%/music/%';");
673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.execSQL("ALTER TABLE audio_meta ADD COLUMN bookmark INTEGER;");
674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // New columns added to tables aren't visible in views on those tables
676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // without opening and closing the database (or using the 'vacuum' command,
677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // which we can't do here because all this code runs inside a transaction).
678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // To work around this, we drop and recreate the affected view and trigger.
679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            recreateAudioView(db);
680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
68195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
6828d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        if (fromVersion < 73) {
6838d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // There is no change to the database schema, but we now do case insensitive
6848d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // matching of folder names when determining whether something is music, a
6858d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            // ringtone, podcast, etc, so we might need to reclassify some files.
6868d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_music=1 WHERE is_music=0 AND " +
6878d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/music/%';");
6888d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_ringtone=1 WHERE is_ringtone=0 AND " +
6898d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/ringtones/%';");
6908d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_notification=1 WHERE is_notification=0 AND " +
6918d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/notifications/%';");
6928d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_alarm=1 WHERE is_alarm=0 AND " +
6938d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/alarms/%';");
6948d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen            db.execSQL("UPDATE audio_meta SET is_podcast=1 WHERE is_podcast=0 AND " +
6958d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen                    "_data LIKE '%/podcasts/%';");
6968d85ef81ed5f1604d40eb8a321fafd2079ada030Marco Nelissen        }
697a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
698a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (fromVersion < 74) {
699a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // This view is used instead of the audio view by the union below, to force
700a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // sqlite to use the title_key index. This greatly reduces memory usage
701a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // (no separate copy pass needed for sorting, which could cause errors on
702a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            // large datasets) and improves speed (by about 35% on a large dataset)
703a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS searchhelpertitle AS SELECT * FROM audio " +
704a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "ORDER BY title_key;");
705a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
706a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            db.execSQL("CREATE VIEW IF NOT EXISTS search AS " +
707a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
708a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'artist' AS mime_type," +
709a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
710a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS album," +
711a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
712a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text1," +
713a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS text2," +
714a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_albums AS data1," +
715a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "number_of_tracks AS data2," +
716a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key AS match," +
717a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/artists/'||_id AS suggest_intent_data," +
718a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "1 AS grouporder " +
719e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM artist_info WHERE (artist!='" + MediaStore.UNKNOWN_STRING + "') " +
720a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
721a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT _id," +
722a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'album' AS mime_type," +
723a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
724a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
725a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS title," +
726a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album AS text1," +
727a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
728a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
729a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
730a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key AS match," +
731a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/albums/'||_id AS suggest_intent_data," +
732a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "2 AS grouporder " +
733e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen                    "FROM album_info WHERE (album!='" + MediaStore.UNKNOWN_STRING + "') " +
734a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "UNION ALL " +
735a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "SELECT searchhelpertitle._id AS _id," +
736a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "mime_type," +
737a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist," +
738a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "album," +
739a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title," +
740a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "title AS text1," +
741a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist AS text2," +
742a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data1," +
743a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "NULL AS data2," +
744a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "artist_key||' '||album_key||' '||title_key AS match," +
745a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "'content://media/external/audio/media/'||searchhelpertitle._id AS " +
746a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "suggest_intent_data," +
747a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "3 AS grouporder " +
748a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    "FROM searchhelpertitle WHERE (title != '') "
749a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                    );
750a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        }
75159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
75259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (fromVersion < 75) {
75395ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen            // Force a rescan of the audio entries so we can apply the new logic to
75459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            // distinguish same-named albums.
75559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("UPDATE audio_meta SET date_modified=0;");
75659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            db.execSQL("DELETE FROM albums");
75759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
75815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen
75915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        if (fromVersion < 76) {
76015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // We now ignore double quotes when building the key, so we have to remove all of them
76115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            // from existing keys.
76215d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE audio_meta SET title_key=" +
76315d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(title_key,x'081D08C29F081D',x'081D') " +
76415d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE title_key LIKE '%'||x'081D08C29F081D'||'%';");
76515d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE albums SET album_key=" +
76615d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(album_key,x'081D08C29F081D',x'081D') " +
76715d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE album_key LIKE '%'||x'081D08C29F081D'||'%';");
76815d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen            db.execSQL("UPDATE artists SET artist_key=" +
76915d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "REPLACE(artist_key,x'081D08C29F081D',x'081D') " +
77015d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen                    "WHERE artist_key LIKE '%'||x'081D08C29F081D'||'%';");
77115d7507838ad66cfebc7d730d143d27ea04736f8Marco Nelissen        }
772b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
773b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (fromVersion < 77) {
774b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // create video thumbnail table
775b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TABLE IF NOT EXISTS videothumbnails (" +
776b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_id INTEGER PRIMARY KEY," +
777b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "_data TEXT," +
778b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "video_id INTEGER," +
779b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "kind INTEGER," +
780b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "width INTEGER," +
781b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "height INTEGER" +
782b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    ");");
783b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
784b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE INDEX IF NOT EXISTS video_id_index on videothumbnails(video_id);");
785b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
786b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            db.execSQL("CREATE TRIGGER IF NOT EXISTS videothumbnails_cleanup DELETE ON videothumbnails " +
787b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "BEGIN " +
788b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "SELECT _DELETE_FILE(old._data);" +
789b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    "END");
790b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
7911769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen
7921769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        if (fromVersion < 78) {
793044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force a rescan of the video entries so we can update
7941769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            // latest changed DATE_TAKEN units (in milliseconds).
7951769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen            db.execSQL("UPDATE video SET date_modified=0;");
7961769168ade7b67f8695a4e4f3c69625aca0811d5Ray Chen        }
797268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
798268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        if (fromVersion < 79) {
799268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // move /sdcard/albumthumbs to
800268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // /sdcard/Android/data/com.android.providers.media/albumthumbs,
801268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            // and update the database accordingly
802268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen
803268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            String storageroot = Environment.getExternalStorageDirectory().getAbsolutePath();
804268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            String oldthumbspath = storageroot + "/albumthumbs";
805268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            String newthumbspath = storageroot + "/" + ALBUM_THUMB_FOLDER;
806268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            File thumbsfolder = new File(oldthumbspath);
807268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            if (thumbsfolder.exists()) {
808268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                // move folder to its new location
809268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                File newthumbsfolder = new File(newthumbspath);
810268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                newthumbsfolder.getParentFile().mkdirs();
811268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                if(thumbsfolder.renameTo(newthumbsfolder)) {
812268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    // update the database
813268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                    db.execSQL("UPDATE album_art SET _data=REPLACE(_data, '" +
814268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                            oldthumbspath + "','" + newthumbspath + "');");
815268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen                }
816268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen            }
817268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen        }
818044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen
819044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        if (fromVersion < 80) {
820044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            // Force rescan of image entries to update DATE_TAKEN as UTC timestamp.
821044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen            db.execSQL("UPDATE images SET date_modified=0;");
822044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen        }
823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void recreateAudioView(SQLiteDatabase db) {
826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Provides a unified audio/artist/album info view.
827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note that views are read-only, so we define a trigger to allow deletes.
828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP VIEW IF EXISTS audio");
829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("DROP TRIGGER IF EXISTS audio_delete");
830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE VIEW IF NOT EXISTS audio as SELECT * FROM audio_meta " +
831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN artists ON audio_meta.artist_id=artists.artist_id " +
832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "LEFT OUTER JOIN albums ON audio_meta.album_id=albums.album_id;");
833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.execSQL("CREATE TRIGGER IF NOT EXISTS audio_delete INSTEAD OF DELETE ON audio " +
835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                "BEGIN " +
836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_meta where _id=old._id;" +
837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_playlists_map where audio_id=old._id;" +
838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "DELETE from audio_genres_map where audio_id=old._id;" +
839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                "END");
840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
84195ff0f28fc2c15fea233e3d2ce71eeea3f1a4942Ray Chen
842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the bucket_id and
844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * bucket_display_name columns are correct.
845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateBucketNames(SQLiteDatabase db, String tableName) {
849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Rebuild the bucket_display_name column using the natural case rather than lower case.
850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA};
853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String data = cursor.getString(dataColumnIndex);
859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ContentValues values = new ContentValues();
860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    computeBucketValues(data, values);
861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    int rowId = cursor.getInt(idColumnIndex);
862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    db.update(tableName, values, "_id=" + rowId, null);
863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Iterate through the rows of a table in a database, ensuring that the
875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * display name column has a value.
876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db
877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param tableName
878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void updateDisplayName(SQLiteDatabase db, String tableName) {
880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Fill in default values for null displayName values
881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] columns = {BaseColumns._ID, MediaColumns.DATA, MediaColumns.DISPLAY_NAME};
884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Cursor cursor = db.query(tableName, columns, null, null, null, null, null);
885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int idColumnIndex = cursor.getColumnIndex(BaseColumns._ID);
887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int dataColumnIndex = cursor.getColumnIndex(MediaColumns.DATA);
888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                final int displayNameIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues();
890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (cursor.moveToNext()) {
891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String displayName = cursor.getString(displayNameIndex);
892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (displayName == null) {
893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = cursor.getString(dataColumnIndex);
894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.clear();
895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        computeDisplayName(data, values);
896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        int rowId = cursor.getInt(idColumnIndex);
897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        db.update(tableName, values, "_id=" + rowId, null);
898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } finally {
901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                cursor.close();
902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the bucked id name and bucket display name are updated.
911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeBucketValues(String data, ContentValues values) {
915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File parentFile = new File(data).getParentFile();
916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (parentFile == null) {
917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            parentFile = new File("/");
918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Lowercase the path for hashing. This avoids duplicate buckets if the
921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // filepath case is changed externally.
922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Keep the original case for display.
923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path = parentFile.toString().toLowerCase();
924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = parentFile.getName();
925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Note: the BUCKET_ID and BUCKET_DISPLAY_NAME attributes are spelled the
927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // same for both images and video. However, for backwards-compatibility reasons
928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // there is no common base class. We use the ImageColumns version here
929702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_ID, path.hashCode());
930702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put(ImageColumns.BUCKET_DISPLAY_NAME, name);
931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param data The input path
935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param values the content values, where the display name is updated.
936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static void computeDisplayName(String data, ContentValues values) {
939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String s = (data == null ? "" : data.toString());
940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int idx = s.lastIndexOf('/');
941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (idx >= 0) {
942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            s = s.substring(idx + 1);
943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        values.put("_display_name", s);
945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
947b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    /**
948b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * This method blocks until thumbnail is ready.
949b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     *
950b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @param thumbUri
951b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     * @return
952b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen     */
953b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean waitForThumbnailReady(Uri origUri) {
954b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Cursor c = this.query(origUri, new String[] { ImageColumns._ID, ImageColumns.DATA,
955b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ImageColumns.MINI_THUMB_MAGIC}, null, null, null);
956b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c == null) return false;
957b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
958b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean result = false;
959b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
960b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (c.moveToFirst()) {
961e263c2a4b880ef8a5314bb4379c74bf5f9292bd0Ray Chen            long id = c.getLong(0);
962b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String path = c.getString(1);
963b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            long magic = c.getLong(2);
964b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
9659299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            MediaThumbRequest req = requestMediaThumbnail(path, origUri,
9669299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    MediaThumbRequest.PRIORITY_HIGH, magic);
9679299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            if (req == null) {
9689299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                return false;
9699299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            }
9709299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen            synchronized (req) {
9719299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                try {
9729299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    while (req.mState == MediaThumbRequest.State.WAIT) {
9739299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        req.wait();
97420434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
9759299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                } catch (InterruptedException e) {
9769299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    Log.w(TAG, e);
9779299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                }
9789299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                if (req.mState == MediaThumbRequest.State.DONE) {
9799299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    result = true;
980b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
981b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
982b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
983b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        c.close();
984b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
985b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return result;
986b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
987b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
988e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    private boolean matchThumbRequest(MediaThumbRequest req, int pid, long id, long gid,
989e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo) {
990e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllOrigId = (id == -1);
991e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        boolean cancelAllGroupId = (gid == -1);
992e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        return (req.mCallingPid == pid) &&
993e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllGroupId || req.mGroupId == gid) &&
994e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (cancelAllOrigId || req.mOrigId == id) &&
995e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                (req.mIsVideo == isVideo);
996e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen    }
997e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
998b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private boolean queryThumbnail(SQLiteQueryBuilder qb, Uri uri, String table,
999b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            String column, boolean hasThumbnailId) {
1000b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        qb.setTables(table);
1001b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (hasThumbnailId) {
1002b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // For uri dispatched to this method, the 4th path segment is always
1003b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // the thumbnail id.
1004b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere("_id = " + uri.getPathSegments().get(3));
1005b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // client already knows which thumbnail it wants, bypass it.
1006b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1007b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1008b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        String origId = uri.getQueryParameter("orig_id");
1009b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // We can't query ready_flag unless we know original id
1010b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId == null) {
1011b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // this could be thumbnail query for other purpose, bypass it.
1012b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return true;
1013b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1014b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1015b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean needBlocking = "1".equals(uri.getQueryParameter("blocking"));
101620434e032e498b716f87cce2f23dd646819218bfRay Chen        boolean cancelRequest = "1".equals(uri.getQueryParameter("cancel"));
1017e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen        Uri origUri = uri.buildUpon().encodedPath(
1018e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                uri.getPath().replaceFirst("thumbnails", "media"))
1019e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                .appendPath(origId).build();
1020b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1021b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (needBlocking && !waitForThumbnailReady(origUri)) {
102220434e032e498b716f87cce2f23dd646819218bfRay Chen            Log.w(TAG, "original media doesn't exist or it's canceled.");
1023b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return false;
102420434e032e498b716f87cce2f23dd646819218bfRay Chen        } else if (cancelRequest) {
1025e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            String groupId = uri.getQueryParameter("group_id");
1026e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            boolean isVideo = "video".equals(uri.getPathSegments().get(1));
102720434e032e498b716f87cce2f23dd646819218bfRay Chen            int pid = Binder.getCallingPid();
102820434e032e498b716f87cce2f23dd646819218bfRay Chen            long id = -1;
1029e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            long gid = -1;
1030e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
103120434e032e498b716f87cce2f23dd646819218bfRay Chen            try {
103220434e032e498b716f87cce2f23dd646819218bfRay Chen                id = Long.parseLong(origId);
1033e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                gid = Long.parseLong(groupId);
103420434e032e498b716f87cce2f23dd646819218bfRay Chen            } catch (NumberFormatException ex) {
103520434e032e498b716f87cce2f23dd646819218bfRay Chen                // invalid cancel request
103620434e032e498b716f87cce2f23dd646819218bfRay Chen                return false;
103720434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1038e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen
103920434e032e498b716f87cce2f23dd646819218bfRay Chen            synchronized (mMediaThumbQueue) {
1040e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                if (mCurrentThumbRequest != null &&
1041e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                        matchThumbRequest(mCurrentThumbRequest, pid, id, gid, isVideo)) {
104220434e032e498b716f87cce2f23dd646819218bfRay Chen                    synchronized (mCurrentThumbRequest) {
104320434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.mState = MediaThumbRequest.State.CANCEL;
104420434e032e498b716f87cce2f23dd646819218bfRay Chen                        mCurrentThumbRequest.notifyAll();
104520434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
104620434e032e498b716f87cce2f23dd646819218bfRay Chen                }
104720434e032e498b716f87cce2f23dd646819218bfRay Chen                for (MediaThumbRequest mtq : mMediaThumbQueue) {
1048e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                    if (matchThumbRequest(mtq, pid, id, gid, isVideo)) {
104920434e032e498b716f87cce2f23dd646819218bfRay Chen                        synchronized (mtq) {
105020434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.mState = MediaThumbRequest.State.CANCEL;
105120434e032e498b716f87cce2f23dd646819218bfRay Chen                            mtq.notifyAll();
105220434e032e498b716f87cce2f23dd646819218bfRay Chen                        }
105320434e032e498b716f87cce2f23dd646819218bfRay Chen
105420434e032e498b716f87cce2f23dd646819218bfRay Chen                        mMediaThumbQueue.remove(mtq);
105520434e032e498b716f87cce2f23dd646819218bfRay Chen                    }
105620434e032e498b716f87cce2f23dd646819218bfRay Chen                }
105720434e032e498b716f87cce2f23dd646819218bfRay Chen            }
1058b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1059b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1060b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        if (origId != null) {
1061b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            qb.appendWhere(column + " = " + origId);
1062b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1063b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        return true;
1064b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1065b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    @SuppressWarnings("fallthrough")
1066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Cursor query(Uri uri, String[] projectionIn, String selection,
1068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] selectionArgs, String sort) {
1069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int table = URI_MATCHER.match(uri);
1070702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
107101a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen        // Log.v(TAG, "query: uri="+uri+", selection="+selection);
1072702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
1073702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (table == MEDIA_SCANNER) {
1074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
1075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return null;
1076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
1077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // create a cursor to return volume currently being scanned by the media scanner
10780027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                MatrixCursor c = new MatrixCursor(new String[] {MediaStore.MEDIA_SCANNER_VOLUME});
10790027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                c.addRow(new String[] {mMediaScannerVolume});
10800027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                return c;
1081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
10840027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // Used temporarily (until we have unique media IDs) to get an identifier
10850027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // for the current sd card, so that the music app doesn't have to use the
10860027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        // non-public getFatVolumeId method
10870027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        if (table == FS_ID) {
10880027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            MatrixCursor c = new MatrixCursor(new String[] {"fsid"});
10890027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            c.addRow(new Integer[] {mVolumeId});
10900027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen            return c;
10910027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        }
10920027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
1093702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String groupBy = null;
1094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
1095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
1096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return null;
1097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1098702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getReadableDatabase();
1099702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
11004574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        String limit = uri.getQueryParameter("limit");
1101b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        boolean hasThumbnailId = false;
1102702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1103702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (table) {
1104702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
1105702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1106702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
1107702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
1108702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1109702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
1110702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
1111702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1112702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1113702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1114702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("images");
1115702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (uri.getQueryParameter("distinct") != null)
1116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    qb.setDistinct(true);
1117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1118702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // set the project map so that data dir is prepended to _data.
1119702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                //qb.setProjectionMap(mImagesProjectionMap, true);
1120702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id = " + uri.getPathSegments().get(3));
1121702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1122702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1123702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
1124b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
1125b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
1126b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "thumbnails", "image_id", hasThumbnailId)) {
1127b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
1128b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1129702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1130702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1131702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1132702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio ");
1133702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1140702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1141702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1142702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT genre_id FROM " +
1143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_genres_map WHERE audio_id = " +
1144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
1148702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1149702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1150702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1151702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1152702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
1153702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1154702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT playlist_id FROM " +
1155702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_playlists_map WHERE audio_id = " +
1156702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1157702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1158702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1159702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
1160702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1161702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1162702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1163702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1164702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
1165702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1166702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1167702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1168702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
1169702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_genres");
1170702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1171702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1172702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1173702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
1174702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1175702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id IN (SELECT audio_id FROM " +
1176702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "audio_genres_map WHERE genre_id = " +
1177702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        uri.getPathSegments().get(3) + ")");
1178702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1179702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1180702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
1181702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1182702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1183702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
1186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists");
1191702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1193702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1194702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
119597e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                if (projectionIn != null) {
119697e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                    for (int i = 0; i < projectionIn.length; i++) {
119797e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        if (projectionIn[i].equals("_id")) {
119897e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                            projectionIn[i] = "audio_playlists_map._id AS _id";
119997e61d89d90a1bd6989a254660aa80553662e002Marco Nelissen                        }
1200702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1201702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1202702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio_playlists_map, audio");
1203702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("audio._id = audio_id AND playlist_id = "
1204702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        + uri.getPathSegments().get(3));
1205702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1206702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1207702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
1208702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio");
1209702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(5));
1210702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1211702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1212702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
1213702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
1214702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1215702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1216702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
1217702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("video");
1218702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1219702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1220702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1221b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
1222b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                hasThumbnailId = true;
1223b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
1224b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (!queryThumbnail(qb, uri, "videothumbnails", "video_id", hasThumbnailId)) {
1225b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    return null;
1226b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1227b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1228b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS:
1230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
1231702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1232702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1233702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID:
1234702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("artist_info");
1235702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ARTISTS_ID_ALBUMS:
1239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String aid = uri.getPathSegments().get(3);
1240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("audio LEFT OUTER JOIN album_art ON" +
1241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " audio.album_id=album_art.album_id");
1242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("is_music=1 AND audio.album_id IN (SELECT album_id FROM " +
1243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "artists_albums_map WHERE artist_id = " +
1244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                         aid + ")");
1245702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                groupBy = "audio.album_id";
1246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                sArtistAlbumsMap.put(MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST,
1247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "count(CASE WHEN artist_id==" + aid + " THEN 'foo' ELSE NULL END) AS " +
1248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        MediaStore.Audio.Albums.NUMBER_OF_SONGS_FOR_ARTIST);
1249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setProjectionMap(sArtistAlbumsMap);
1250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1251702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS:
1253702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
1254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1256702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMS_ID:
1257702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_info");
1258702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("_id=" + uri.getPathSegments().get(3));
1259702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1260702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1261702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
1262702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.setTables("album_art");
1263702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + uri.getPathSegments().get(3));
1264702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1265702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1266a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_LEGACY:
1267a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                Log.w(TAG, "Legacy media search Uri used. Please update your code.");
1268a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                // fall through
1269a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_FANCY:
1270a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            case AUDIO_SEARCH_BASIC:
1271a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                return doAudioSearch(db, qb, uri, projectionIn, selection, selectionArgs, sort,
12724574e03055af60fada50481f2b34e19a687d5866Marco Nelissen                        table, limit);
1273702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1274702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
1275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalStateException("Unknown URL: " + uri.toString());
1276702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1277702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
12784d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen        // Log.v(TAG, "query = "+ qb.buildQuery(projectionIn, selection, selectionArgs, groupBy, null, sort, limit));
1279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = qb.query(db, projectionIn, selection,
12804574e03055af60fada50481f2b34e19a687d5866Marco Nelissen                selectionArgs, groupBy, null, sort, limit);
1281b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (c != null) {
1283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            c.setNotificationUri(getContext().getContentResolver(), uri);
1284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1285b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return c;
1287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Cursor doAudioSearch(SQLiteDatabase db, SQLiteQueryBuilder qb,
1290702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            Uri uri, String[] projectionIn, String selection,
12914574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String[] selectionArgs, String sort, int mode,
12924574e03055af60fada50481f2b34e19a687d5866Marco Nelissen            String limit) {
1293702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1294a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String mSearchString = uri.toString().endsWith("/") ? "" : uri.getLastPathSegment();
1295702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        mSearchString = mSearchString.replaceAll("  ", " ").trim().toLowerCase();
1296702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1297702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] searchWords = mSearchString.length() > 0 ?
1298702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                mSearchString.split(" ") : new String[0];
1299a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] wildcardWords = new String[searchWords.length];
1300702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Collator col = Collator.getInstance();
1301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        col.setStrength(Collator.PRIMARY);
1302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int len = searchWords.length;
1303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        for (int i = 0; i < len; i++) {
1304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Because we match on individual words here, we need to remove words
1305702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // like 'a' and 'the' that aren't part of the keys.
1306a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            wildcardWords[i] =
1307702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                (searchWords[i].equals("a") || searchWords[i].equals("an") ||
1308702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        searchWords[i].equals("the")) ? "%" :
1309702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                '%' + MediaStore.Audio.keyFor(searchWords[i]) + '%';
1310702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1311702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1312a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String where = "";
1313a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        for (int i = 0; i < searchWords.length; i++) {
1314a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            if (i == 0) {
1315a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                where = "match LIKE ?";
1316a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            } else {
1317a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                where += " AND match LIKE ?";
1318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1321a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        qb.setTables("search");
1322a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        String [] cols;
1323a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        if (mode == AUDIO_SEARCH_FANCY) {
1324a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsFancy;
1325a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else if (mode == AUDIO_SEARCH_BASIC) {
1326a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsBasic;
1327a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        } else {
1328a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen            cols = mSearchColsLegacy;
1329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
13304574e03055af60fada50481f2b34e19a687d5866Marco Nelissen        return qb.query(db, cols, where, wildcardWords, null, null, null, limit);
1331702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1332702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public String getType(Uri url)
1335702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
1336702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (URI_MATCHER.match(url)) {
1337702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1338702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1339702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
1340702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
1341702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
1342702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = query(url, MIME_TYPE_PROJECTION, null, null, null);
1343702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (c != null && c.getCount() == 1) {
1344702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    c.moveToFirst();
1345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String mimeType = c.getString(1);
1346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    c.deactivate();
1347702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    return mimeType;
1348702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1349702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1350702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1351702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA:
1352702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS:
1353702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Images.Media.CONTENT_TYPE;
1354702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS_ID:
1355702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return "image/jpeg";
1356702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1357702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1358702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
1359702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
1360702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Media.CONTENT_TYPE;
1361702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1362702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
1363702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1364702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.CONTENT_TYPE;
1365702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
1366702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
1367702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Genres.ENTRY_CONTENT_TYPE;
1368702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
1369702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
1370702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.CONTENT_TYPE;
1371702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1372702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
1373702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Audio.Playlists.ENTRY_CONTENT_TYPE;
1374702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1375702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
1376702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Video.Media.CONTENT_TYPE;
1377702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1378702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        throw new IllegalStateException("Unknown URL");
1379702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1380702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1381702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
1382702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Ensures there is a file in the _data column of values, if one isn't
1383702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * present a new file is created.
1384702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
1385702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param initialValues the values passed to insert by the caller
1386702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the new values
1387702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
1388702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private ContentValues ensureFile(boolean internal, ContentValues initialValues,
1389702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String preferredExtension, String directoryName) {
1390702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ContentValues values;
1391702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String file = initialValues.getAsString("_data");
1392702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (TextUtils.isEmpty(file)) {
1393702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file = generateFileName(internal, preferredExtension, directoryName);
1394702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = new ContentValues(initialValues);
1395702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values.put("_data", file);
1396702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1397702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            values = initialValues;
1398702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1399702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1400702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!ensureFileExists(file)) {
1401702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalStateException("Unable to create new file: " + file);
1402702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1403702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return values;
1404702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1405702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1406702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1407702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int bulkInsert(Uri uri, ContentValues values[]) {
1408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
1409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == VOLUMES) {
1410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return super.bulkInsert(uri, values);
1411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
1413702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
1414702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
1415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
1416702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1417702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getWritableDatabase();
1418ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
1419ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        if (match == AUDIO_PLAYLISTS_ID || match == AUDIO_PLAYLISTS_ID_MEMBERS) {
1420ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            return playlistBulkInsert(db, uri, values);
1421ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
1422ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
1423702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        db.beginTransaction();
1424702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int numInserted = 0;
1425702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
1426702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int len = values.length;
1427702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            for (int i = 0; i < len; i++) {
1428702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                insertInternal(uri, values[i]);
1429702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1430702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            numInserted = len;
1431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.setTransactionSuccessful();
1432702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
1433702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            db.endTransaction();
1434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1435702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
1436702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return numInserted;
1437702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1438702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1439702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1440702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public Uri insert(Uri uri, ContentValues initialValues)
1441702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
1442702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = insertInternal(uri, initialValues);
1443702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (newUri != null) {
1444702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
1445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1446702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
1447702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1448702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1449ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    private int playlistBulkInsert(SQLiteDatabase db, Uri uri, ContentValues values[]) {
1450ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        DatabaseUtils.InsertHelper helper =
1451ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            new DatabaseUtils.InsertHelper(db, "audio_playlists_map");
14528b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int audioidcolidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
14538b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playlistididx = helper.getColumnIndex(Audio.Playlists.Members.PLAYLIST_ID);
14548b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        int playorderidx = helper.getColumnIndex(MediaStore.Audio.Playlists.Members.PLAY_ORDER);
14558b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen        long playlistId = Long.parseLong(uri.getPathSegments().get(3));
1456ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
1457ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        db.beginTransaction();
1458ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        int numInserted = 0;
1459ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        try {
1460ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            int len = values.length;
1461ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            for (int i = 0; i < len; i++) {
14628b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.prepareForInsert();
14638b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // getting the raw Object and converting it long ourselves saves
14648b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // an allocation (the alternative is ContentValues.getAsLong, which
14658b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // returns a Long object)
14668b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                long audioid = ((Number) values[i].get(
14678b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.AUDIO_ID)).longValue();
14688b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(audioidcolidx, audioid);
14698b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playlistididx, playlistId);
14708b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                // convert to int ourselves to save an allocation.
14718b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                int playorder = ((Number) values[i].get(
14728b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                        MediaStore.Audio.Playlists.Members.PLAY_ORDER)).intValue();
14738b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.bind(playorderidx, playorder);
14748b29060b0563b6a17b21b909bc4a9af14636021cMarco Nelissen                helper.execute();
1475ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            }
1476ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            numInserted = len;
1477ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.setTransactionSuccessful();
1478ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        } finally {
1479ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            db.endTransaction();
1480ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen            helper.close();
1481ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        }
1482ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        getContext().getContentResolver().notifyChange(uri, null);
1483ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen        return numInserted;
1484ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen    }
1485ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen
1486702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri insertInternal(Uri uri, ContentValues initialValues) {
1487702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
1488702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
1489702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1490b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "insertInternal: "+uri+", initValues="+initialValues);
1491702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
1492702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
1493702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = initialValues.getAsString(MediaStore.MEDIA_SCANNER_VOLUME);
1494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return MediaStore.getMediaScannerUri();
1495702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1496702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1497702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri newUri = null;
1498702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
1499702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null && match != VOLUMES) {
1500702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
1501702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
1502702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1503702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = (match == VOLUMES ? null : database.getWritableDatabase());
1504702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1505702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (initialValues == null) {
1506702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            initialValues = new ContentValues();
1507702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1508702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1509702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
1510702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA: {
1511702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg", "DCIM/Camera");
1512702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1513702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
1514702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String data = values.getAsString(MediaColumns.DATA);
1515702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (! values.containsKey(MediaColumns.DISPLAY_NAME)) {
1516702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    computeDisplayName(data, values);
1517702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1518702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeBucketValues(data, values);
1519702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("images", "name", values);
1520702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1521702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1522702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(
1523702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Images.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
15249299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    requestMediaThumbnail(data, newUri, MediaThumbRequest.PRIORITY_NORMAL, 0);
1525702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1526702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1527702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1528702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1529b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This will be triggered by requestMediaThumbnail (see getThumbnailUri)
1530702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_THUMBNAILS: {
1531b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg",
1532b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
1533702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("thumbnails", "name", values);
1534702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1535702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Images.Thumbnails.
1536702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContentUri(uri.getPathSegments().get(0)), rowId);
1537702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1538702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1539702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1540702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1541b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            // This is currently only used by MICRO_KIND video thumbnail (see getThumbnailUri)
1542b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS: {
1543b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                ContentValues values = ensureFile(database.mInternal, initialValues, ".jpg",
1544b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        "DCIM/.thumbnails");
1545b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                rowId = db.insert("videothumbnails", "name", values);
1546b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                if (rowId > 0) {
1547b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Thumbnails.
1548b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            getContentUri(uri.getPathSegments().get(0)), rowId);
1549b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                }
1550b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1551b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            }
1552b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1553702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA: {
1554702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // SQLite Views are read-only, so we need to deconstruct this
1555702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // insert and do inserts into the underlying tables.
1556702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // If doing this here turns out to be a performance bottleneck,
1557702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // consider moving this to native code and using triggers on
1558702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // the view.
1559702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1560702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1561702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Insert the artist into the artist table and remove it from
1562702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // the input values
1563702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Object so = values.get("artist");
1564702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String s = (so == null ? "" : so.toString());
1565702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.remove("artist");
1566702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long artistRowId;
1567702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashMap<String, Long> artistCache = database.mArtistCache;
156859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                String path = values.getAsString("_data");
1569702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                synchronized(artistCache) {
1570702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Long temp = artistCache.get(s);
1571702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (temp == null) {
1572702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        artistRowId = getKeyIdForName(db, "artists", "artist_key", "artist",
1573a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                s, s, path, 0, null, artistCache, uri);
1574702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    } else {
1575702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        artistRowId = temp.longValue();
1576702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1577702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1578a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                String artist = s;
1579702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Do the same for the album field
1581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                so = values.get("album");
1582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                s = (so == null ? "" : so.toString());
1583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.remove("album");
1584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                long albumRowId;
1585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashMap<String, Long> albumCache = database.mAlbumCache;
1586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                synchronized(albumCache) {
158759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    int albumhash = path.substring(0, path.lastIndexOf('/')).hashCode();
158859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    String cacheName = s + albumhash;
158959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                    Long temp = albumCache.get(cacheName);
1590702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (temp == null) {
1591702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        albumRowId = getKeyIdForName(db, "albums", "album_key", "album",
1592a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                s, cacheName, path, albumhash, artist, albumCache, uri);
1593702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    } else {
1594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        albumRowId = temp;
1595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
1596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("artist_id", Integer.toString((int)artistRowId));
1599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("album_id", Integer.toString((int)albumRowId));
1600702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                so = values.getAsString("title");
1601702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                s = (so == null ? "" : so.toString());
1602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put("title_key", MediaStore.Audio.keyFor(s));
1603358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                // do a final trim of the title, in case it started with the special
1604358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                // "sort first" character (ascii \001)
1605358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                values.remove("title");
1606358cfed9391de9b39ecb2a3dbefcf5e392915954Marco Nelissen                values.put("title", s.trim());
1607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeDisplayName(values.getAsString("_data"), values);
1609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
1610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_meta", "duration", values);
1612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1613702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Media.getContentUri(uri.getPathSegments().get(0)), rowId);
1614702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1615702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1616702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1617702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1618702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES: {
1619702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
1620702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1621702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.AUDIO_ID, audioId);
1622ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_genres_map", "genre_id", values);
1623702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1624702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1625702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1626702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1627702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1628702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS: {
1630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long audioId = Long.parseLong(uri.getPathSegments().get(2));
1631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.AUDIO_ID, audioId);
1633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists_map", "playlist_id",
1634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values);
1635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1640702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES: {
1642702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres", "audio_id", initialValues);
1643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Genres.getContentUri(uri.getPathSegments().get(0)), rowId);
1645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS: {
1650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long genreId = Long.parseLong(uri.getPathSegments().get(3));
1651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Genres.Members.GENRE_ID, genreId);
1653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_genres_map", "genre_id", values);
1654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS: {
1661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.Audio.Playlists.DATE_ADDED, System.currentTimeMillis() / 1000);
1663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("audio_playlists", "name", initialValues);
1664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(Audio.Playlists.getContentUri(uri.getPathSegments().get(0)), rowId);
1666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS: {
1672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Long playlistId = Long.parseLong(uri.getPathSegments().get(3));
1673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = new ContentValues(initialValues);
1674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(Audio.Playlists.Members.PLAYLIST_ID, playlistId);
1675ccf3e3c938fa9777cb6297b4e910cb6a58558671Marco Nelissen                rowId = db.insert("audio_playlists_map", "playlist_id", values);
1676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1681702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1682702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA: {
1683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = ensureFile(database.mInternal, initialValues, ".3gp", "video");
1684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String data = values.getAsString("_data");
1685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeDisplayName(data, values);
1686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                computeBucketValues(data, values);
1687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis() / 1000);
1688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("video", "artist", values);
1689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1690b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                    newUri = ContentUris.withAppendedId(Video.Media.getContentUri(
1691b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            uri.getPathSegments().get(0)), rowId);
16929299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                    requestMediaThumbnail(data, newUri, MediaThumbRequest.PRIORITY_NORMAL, 0);
1693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART:
1698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database.mInternal) {
1699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw new UnsupportedOperationException("no internal album art allowed");
1700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                ContentValues values = null;
1702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
1703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
1704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } catch (IllegalStateException ex) {
1705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // probably no more room to store albumthumbs
1706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    values = initialValues;
1707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                rowId = db.insert("album_art", "_data", values);
1709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (rowId > 0) {
1710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    newUri = ContentUris.withAppendedId(uri, rowId);
1711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VOLUMES:
1715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return attachVolume(initialValues.getAsString("name"));
1716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
1718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException("Invalid URI " + uri);
1719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return newUri;
1722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1724cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    @Override
1725cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
1726cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                throws OperationApplicationException {
1727cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
1728cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // The operations array provides no overall information about the URI(s) being operated
1729cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // on, so begin a transaction for ALL of the databases.
1730cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ihelper = getDatabaseForUri(MediaStore.Audio.Media.INTERNAL_CONTENT_URI);
1731cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        DatabaseHelper ehelper = getDatabaseForUri(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI);
1732cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase idb = ihelper.getWritableDatabase();
1733cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        idb.beginTransaction();
1734cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        SQLiteDatabase edb = null;
1735cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (ehelper != null) {
1736cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb = ehelper.getWritableDatabase();
1737cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            edb.beginTransaction();
1738cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
1739cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        try {
1740cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentProviderResult[] result = super.applyBatch(operations);
1741cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.setTransactionSuccessful();
1742cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
1743cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.setTransactionSuccessful();
1744cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
1745cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // Rather than sending targeted change notifications for every Uri
1746cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // affected by the batch operation, just invalidate the entire internal
1747cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            // and external name space.
1748cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            ContentResolver res = getContext().getContentResolver();
1749cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            res.notifyChange(Uri.parse("content://media/"), null);
1750cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            return result;
1751cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        } finally {
1752cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            idb.endTransaction();
1753cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            if (edb != null) {
1754cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen                edb.endTransaction();
1755cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen            }
1756cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        }
1757cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen    }
1758cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
1759cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen
17609299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen    private MediaThumbRequest requestMediaThumbnail(String path, Uri uri, int priority, long magic) {
1761b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        synchronized (mMediaThumbQueue) {
1762e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            MediaThumbRequest req = null;
1763e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            try {
1764e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                req = new MediaThumbRequest(
17659299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                        getContext().getContentResolver(), path, uri, priority, magic);
1766e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                mMediaThumbQueue.add(req);
1767e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                // Trigger the handler.
1768e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Message msg = mThumbHandler.obtainMessage(IMAGE_THUMB);
1769e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                msg.sendToTarget();
1770e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            } catch (Throwable t) {
1771e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen                Log.w(TAG, t);
1772e7219b83c17d75151746d6cad1b61d447910ae92Ray Chen            }
1773b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            return req;
1774b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        }
1775b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    }
1776b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String generateFileName(boolean internal, String preferredExtension, String directoryName)
1778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
1779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // create a random file
1780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name = String.valueOf(System.currentTimeMillis());
1781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (internal) {
1783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException("Writing to internal storage is not supported.");
1784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//            return Environment.getDataDirectory()
1785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project//                + "/" + directoryName + "/" + name + preferredExtension;
1786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return Environment.getExternalStorageDirectory()
1788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                + "/" + directoryName + "/" + name + preferredExtension;
1789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private boolean ensureFileExists(String path) {
1793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        File file = new File(path);
1794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (file.exists()) {
1795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return true;
1796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // we will not attempt to create the first directory in the path
1798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // (for example, do not create /sdcard if the SD card is not mounted)
1799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            int secondSlash = path.indexOf('/', 1);
1800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (secondSlash < 1) return false;
1801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String directoryPath = path.substring(0, secondSlash);
1802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File directory = new File(directoryPath);
1803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!directory.exists())
1804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return false;
1805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            file.getParentFile().mkdirs();
1806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
1807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return file.createNewFile();
1808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch(IOException ioe) {
1809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "File creation failed", ioe);
1810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return false;
1812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final class GetTableAndWhereOutParameter {
1816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String table;
1817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        public String where;
1818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final GetTableAndWhereOutParameter sGetTableAndWhereParam =
1821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new GetTableAndWhereOutParameter();
1822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void getTableAndWhere(Uri uri, int match, String userWhere,
1824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            GetTableAndWhereOutParameter out) {
1825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String where = null;
1826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        switch (match) {
18279f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin            case IMAGES_MEDIA:
18289f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                out.table = "images";
18299f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin                break;
18309f02f449f6951b15bb9daaa3fc6e1d648b36b08aGoodwin
1831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case IMAGES_MEDIA_ID:
1832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "images";
1833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id = " + uri.getPathSegments().get(3);
1834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1836b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS_ID:
1837b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
1838b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case IMAGES_THUMBNAILS:
1839b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "thumbnails";
1840b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1841b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA:
1843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio";
1844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID:
1847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio";
1848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
1849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES:
1852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
1854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_GENRES_ID:
1857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
1859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND genre_id=" + uri.getPathSegments().get(5);
1860702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project               break;
1861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS:
1863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
1864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3);
1865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_MEDIA_ID_PLAYLISTS_ID:
1868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
1869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "audio_id=" + uri.getPathSegments().get(3) +
1870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND playlists_id=" + uri.getPathSegments().get(5);
1871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES:
1874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1877702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID:
1878702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1879702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
1880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1882702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS:
1883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3);
1885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_GENRES_ID_MEMBERS_ID:
1888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_genres";
1889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "genre_id=" + uri.getPathSegments().get(3) +
1890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND audio_id =" + uri.getPathSegments().get(5);
1891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS:
1894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
1895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID:
1898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists";
1899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
1900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS:
1903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
1904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3);
1905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
1908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "audio_playlists_map";
1909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "playlist_id=" + uri.getPathSegments().get(3) +
1910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        " AND _id=" + uri.getPathSegments().get(5);
1911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1913702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case AUDIO_ALBUMART_ID:
1914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "album_art";
1915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "album_id=" + uri.getPathSegments().get(3);
1916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1917702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1918702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA:
1919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "video";
1920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            case VIDEO_MEDIA_ID:
1923702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.table = "video";
1924702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                where = "_id=" + uri.getPathSegments().get(3);
1925702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                break;
1926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1927b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS_ID:
1928b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                where = "_id=" + uri.getPathSegments().get(3);
1929b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            case VIDEO_THUMBNAILS:
1930b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                out.table = "videothumbnails";
1931b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                break;
1932b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
1933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            default:
1934702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
1935702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown or unsupported URL: " + uri.toString());
1936702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1937702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Add in the user requested WHERE clause, if needed
1939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (!TextUtils.isEmpty(userWhere)) {
1940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!TextUtils.isEmpty(where)) {
1941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = where + " AND (" + userWhere + ")";
1942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
1943702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                out.where = userWhere;
1944702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1945702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1946702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            out.where = where;
1947702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1948702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int delete(Uri uri, String userWhere, String[] whereArgs) {
1952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
1953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
1954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // handle MEDIA_SCANNER before calling getDatabaseForUri()
1956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match == MEDIA_SCANNER) {
1957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mMediaScannerVolume == null) {
1958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return 0;
1959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mMediaScannerVolume = null;
1961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return 1;
1962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (match != VOLUMES_ID) {
1965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = getDatabaseForUri(uri);
1966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) {
1967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new UnsupportedOperationException(
1968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        "Unknown URI: " + uri);
1969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            SQLiteDatabase db = database.getWritableDatabase();
1971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            synchronized (sGetTableAndWhereParam) {
1973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
1974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                switch (match) {
1975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    case AUDIO_MEDIA:
1976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    case AUDIO_MEDIA_ID:
1977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete("audio_meta",
1978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
1979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
1980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    default:
1981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.delete(sGetTableAndWhereParam.table,
1982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
1983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        break;
1984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
1985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                getContext().getContentResolver().notifyChange(uri, null);
1986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
1987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
1988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            detachVolume(uri);
1989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            count = 1;
1990702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
1991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
1993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
1994702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
1995702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
1996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public int update(Uri uri, ContentValues initialValues, String userWhere,
1997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String[] whereArgs) {
1998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int count;
1999b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        // Log.v(TAG, "update for uri="+uri+", initValues="+initialValues);
2000702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        int match = URI_MATCHER.match(uri);
2001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        DatabaseHelper database = getDatabaseForUri(uri);
2002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (database == null) {
2003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2004702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Unknown URI: " + uri);
2005702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2006702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db = database.getWritableDatabase();
2007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2008702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (sGetTableAndWhereParam) {
2009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getTableAndWhere(uri, match, userWhere, sGetTableAndWhereParam);
2010702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2011702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (match) {
2012702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA:
2013702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case AUDIO_MEDIA_ID:
2014702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
2015702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
2016702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Insert the artist into the artist table and remove it from
2017702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // the input values
2018a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String artist = values.getAsString("artist");
2019a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        if (artist != null) {
2020702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.remove("artist");
2021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long artistRowId;
2022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            HashMap<String, Long> artistCache = database.mArtistCache;
2023702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(artistCache) {
2024a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                Long temp = artistCache.get(artist);
2025702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
2026702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = getKeyIdForName(db, "artists", "artist_key", "artist",
2027a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            artist, artist, null, 0, null, artistCache, uri);
2028702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
2029702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    artistRowId = temp.longValue();
2030702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
2031702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
2032702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("artist_id", Integer.toString((int)artistRowId));
2033702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2034702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
203559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        // Do the same for the album field.
2036a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                        String so = values.getAsString("album");
2037702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
203859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            String path = values.getAsString("_data");
203959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            int albumHash = 0;
204059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            if (path == null) {
204159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                // If the path is null, we don't have a hash for the file in question.
204259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Log.w(TAG, "Update without specified path.");
204359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            } else {
204459948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                albumHash = path.substring(0, path.lastIndexOf('/')).hashCode();
204559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                            }
2046702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
2047702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.remove("album");
2048702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            long albumRowId;
2049702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            HashMap<String, Long> albumCache = database.mAlbumCache;
2050702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            synchronized(albumCache) {
205159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                String cacheName = s + albumHash;
205259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                                Long temp = albumCache.get(cacheName);
2053702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                if (temp == null) {
2054702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = getKeyIdForName(db, "albums", "album_key", "album",
2055a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                                            s, cacheName, path, albumHash, artist, albumCache, uri);
2056702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                } else {
2057702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                    albumRowId = temp.longValue();
2058702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                }
2059702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
2060702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("album_id", Integer.toString((int)albumRowId));
2061702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2062702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2063702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // don't allow the title_key field to be updated directly
2064702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove("title_key");
2065702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the title field is modified, update the title_key
2066702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        so = values.getAsString("title");
2067702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (so != null) {
2068702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String s = so.toString();
2069702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            values.put("title_key", MediaStore.Audio.keyFor(s));
2070e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // do a final trim of the title, in case it started with the special
2071e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            // "sort first" character (ascii \001)
2072e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.remove("title");
2073e96c1d9637e89b5f99c7002fd06f7f35a9164849Marco Nelissen                            values.put("title", s.trim());
2074702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2075702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2076702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update("audio_meta", values, sGetTableAndWhereParam.where,
2077702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                whereArgs);
2078702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2079702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2080702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA:
2081702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case IMAGES_MEDIA_ID:
2082702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA:
2083702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case VIDEO_MEDIA_ID:
2084702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    {
2085702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues values = new ContentValues(initialValues);
2086702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Don't allow bucket id or display name to be updated directly.
2087702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // The same names are used for both images and table columns, so
2088702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // we use the ImageColumns constants here.
2089702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_ID);
2090702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        values.remove(ImageColumns.BUCKET_DISPLAY_NAME);
2091702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // If the data is being modified update the bucket values
2092702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String data = values.getAsString(MediaColumns.DATA);
2093702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (data != null) {
2094702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            computeBucketValues(data, values);
2095702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2096702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        count = db.update(sGetTableAndWhereParam.table, values,
2097702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                                sGetTableAndWhereParam.where, whereArgs);
209801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // if this is a request from MediaScanner, DATA should contains file path
209901a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // we only process update request from media scanner, otherwise the requests
210001a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        // could be duplicate.
210101a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                        if (count > 0 && values.getAsString(MediaStore.MediaColumns.DATA) != null) {
210201a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                            Cursor c = db.query(sGetTableAndWhereParam.table,
210301a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    READY_FLAG_PROJECTION, sGetTableAndWhereParam.where,
210401a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    whereArgs, null, null, null);
2105b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            if (c != null) {
2106b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                while (c.moveToNext()) {
210701a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    long magic = c.getLong(2);
210801a6f2f96c5b483f5281f6d3066380a129c06021Ray Chen                                    if (magic == 0) {
21094d96d72ea42c2ec41a891f65623270473ae8eebdRay Chen                                        requestMediaThumbnail(c.getString(1), uri,
21109299727714ad25618a1a77eeca7f1e9c72f1e510Ray Chen                                                MediaThumbRequest.PRIORITY_NORMAL, 0);
2111b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                    }
2112b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                }
2113b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                                c.close();
2114b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                            }
2115b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen                        }
2116702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2117702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2118f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
2119f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                case AUDIO_PLAYLISTS_ID_MEMBERS_ID:
2120f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    String moveit = uri.getQueryParameter("move");
2121f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    if (moveit != null) {
2122f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        String key = MediaStore.Audio.Playlists.Members.PLAY_ORDER;
2123f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        if (initialValues.containsKey(key)) {
2124f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int newpos = initialValues.getAsInteger(key);
2125f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            List <String> segments = uri.getPathSegments();
2126f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            long playlist = Long.valueOf(segments.get(3));
2127f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            int oldpos = Integer.valueOf(segments.get(5));
2128f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                            return movePlaylistEntry(db, playlist, oldpos, newpos);
2129f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        }
2130f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        throw new IllegalArgumentException("Need to specify " + key +
2131f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                                " when using 'move' parameter");
2132f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    }
2133f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    // fall through
2134702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
2135702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    count = db.update(sGetTableAndWhereParam.table, initialValues,
2136702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        sGetTableAndWhereParam.where, whereArgs);
2137702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2138702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2139702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2140cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // in a transaction, the code that began the transaction should be taking
2141cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        // care of notifications once it ends the transaction successfully
2142cb0c5a6863b073d142b1fd3b4168cd665b72ae80Marco Nelissen        if (count > 0 && !db.inTransaction()) {
2143702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            getContext().getContentResolver().notifyChange(uri, null);
2144702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2145702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return count;
2146702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2147702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2148f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    private int movePlaylistEntry(SQLiteDatabase db, long playlist, int from, int to) {
2149f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        if (from == to) {
2150f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return 0;
2151f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
2152f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        db.beginTransaction();
2153f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        try {
2154f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            int numlines = 0;
2155f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=-1" +
2156f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=" + from +
2157f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " AND playlist_id=" + playlist);
2158f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // We could just run both of the next two statements, but only one of
2159f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // of them will actually do anything, so might as well skip the compile
2160f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            // and execute steps.
2161f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            if (from  < to) {
2162f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order-1" +
2163f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " WHERE play_order<=" + to + " AND play_order>" + from +
2164f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
2165f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = to - from + 1;
2166f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            } else {
2167f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                db.execSQL("UPDATE audio_playlists_map SET play_order=play_order+1" +
2168f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " WHERE play_order>=" + to + " AND play_order<" + from +
2169f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                        " AND playlist_id=" + playlist);
2170f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                numlines = from - to + 1;
2171f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            }
2172f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.execSQL("UPDATE audio_playlists_map SET play_order=" + to +
2173f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    " WHERE play_order=-1 AND playlist_id=" + playlist);
2174f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.setTransactionSuccessful();
2175f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
2176f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen                    .buildUpon().appendEncodedPath(String.valueOf(playlist)).build();
2177f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            getContext().getContentResolver().notifyChange(uri, null);
2178f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            return numlines;
2179f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        } finally {
2180f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen            db.endTransaction();
2181f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen        }
2182f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen    }
2183f5f9eca3e7237c0aa12ea9e58bd980af041adbc4Marco Nelissen
2184702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] openFileColumns = new String[] {
2185702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        MediaStore.MediaColumns.DATA,
2186702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
2187702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2188702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    @Override
2189702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    public ParcelFileDescriptor openFile(Uri uri, String mode)
2190702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throws FileNotFoundException {
219171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
2192702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ParcelFileDescriptor pfd = null;
219371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
219471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_FILE_ID) {
219571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // get album art for the specified media file
219671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            DatabaseHelper database = getDatabaseForUri(uri);
219771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (database == null) {
219871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw new IllegalStateException("Couldn't open database for " + uri);
219971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
220071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteDatabase db = database.getReadableDatabase();
220171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
220271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            int songid = Integer.parseInt(uri.getPathSegments().get(3));
220371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.setTables("audio_meta");
220471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            qb.appendWhere("_id=" + songid);
220571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Cursor c = qb.query(db,
220671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    new String [] {
220771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.DATA,
220871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                        MediaStore.Audio.Media.ALBUM_ID },
220971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    null, null, null, null, null);
221071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
221171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                String audiopath = c.getString(0);
221271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                int albumid = c.getInt(1);
221371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // Try to get existing album art for this album first, which
221471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // could possibly have been obtained from a different file.
221571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // If that fails, try to get it from this specific file.
221671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                Uri newUri = ContentUris.withAppendedId(ALBUMART_URI, albumid);
221771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                try {
221871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = openFile(newUri, mode);  // recursive call
221971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                } catch (FileNotFoundException ex) {
222071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    // That didn't work, now try to get it from the specific file
222171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, null);
222271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
222371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
222471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            c.close();
222571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return pfd;
222671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
222771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
2228702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2229702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd = openFileHelper(uri, mode);
2230702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (FileNotFoundException ex) {
223171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (mode.contains("w")) {
223271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                // if the file couldn't be created, we shouldn't extract album art
223371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
223471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
223571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
2236702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (URI_MATCHER.match(uri) == AUDIO_ALBUMART_ID) {
2237702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // Tried to open an album art file which does not exist. Regenerate.
2238702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                DatabaseHelper database = getDatabaseForUri(uri);
2239702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (database == null) {
2240702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    throw ex;
2241702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2242702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteDatabase db = database.getReadableDatabase();
2243702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
2244702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int albumid = Integer.parseInt(uri.getPathSegments().get(3));
224571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                qb.setTables("audio_meta");
2246702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                qb.appendWhere("album_id=" + albumid);
2247702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor c = qb.query(db,
2248702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String [] {
2249702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            MediaStore.Audio.Media.DATA },
2250702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        null, null, null, null, null);
225171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                if (c.moveToFirst()) {
2252702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String audiopath = c.getString(0);
225371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                    pfd = getThumb(db, audiopath, albumid, uri);
2254702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2255702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                c.close();
225695d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen            } else {
225795d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                // Opening the file failed. See if we can interpret the path as
225895d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                // a Uri and open that instead. This lets items in the media
225995d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                // database transparently refer to items in other content providers.
226095d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                // TODO figure out some way to detect recursion and abort
226195d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                // (using ThreadLocal might be appropriate for that).
226295d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                ContentResolver res = getContext().getContentResolver();
226395d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                Cursor c = res.query(uri, new String[] {"_data"}, null, null, null);
226495d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                if (c != null) {
226595d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                    try {
226695d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                        if (c.moveToFirst()) {
226795d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                            String path = c.getString(0);
226895d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                            Uri newUri = Uri.parse(path);
226995d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                            return res.openFileDescriptor(newUri, mode);
227095d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                        }
227195d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                    } finally {
227295d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                        c.close();
227395d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                    }
227495d21e8f20bbddc5495051771ae2a4f19da73332Marco Nelissen                }
2275702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
227671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (pfd == null) {
227771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                throw ex;
227871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
2279702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2280702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return pfd;
2281702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2282702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2283702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private class ThumbData {
2284702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        SQLiteDatabase db;
2285702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String path;
2286702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long album_id;
2287702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Uri albumart_uri;
2288702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2289702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2290a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen    private void makeThumbAsync(SQLiteDatabase db, String path, long album_id) {
22918a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mPendingThumbs) {
22928a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (mPendingThumbs.contains(path)) {
22938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // There's already a request to make an album art thumbnail
22948a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // for this audio file in the queue.
22958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                return;
22968a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
22978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
22988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mPendingThumbs.add(path);
22998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
23008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
2301702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        ThumbData d = new ThumbData();
2302702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.db = db;
2303702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.path = path;
2304702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        d.album_id = album_id;
2305a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen        d.albumart_uri = ContentUris.withAppendedId(mAlbumArtBaseUri, album_id);
23068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
23078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Instead of processing thumbnail requests in the order they were
23088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // received we instead process them stack-based, i.e. LIFO.
23098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // The idea behind this is that the most recently requested thumbnails
23108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // are most likely the ones still in the user's view, whereas those
23118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // requested earlier may have already scrolled off.
23128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        synchronized (mThumbRequestStack) {
23138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            mThumbRequestStack.push(d);
23148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
23158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
23168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // Trigger the handler.
2317b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        Message msg = mThumbHandler.obtainMessage(ALBUM_THUMB);
2318702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        msg.sendToTarget();
2319702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2320702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
23218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Extract compressed image data from the audio file itself or, if that fails,
23228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // look for a file "AlbumArt.jpg" in the containing directory.
23238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private static byte[] getCompressedAlbumArt(Context context, String path) {
23248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = null;
2325702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2326702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2327702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            File f = new File(path);
2328702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            ParcelFileDescriptor pfd = ParcelFileDescriptor.open(f,
2329702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    ParcelFileDescriptor.MODE_READ_ONLY);
2330702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
23318a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            MediaScanner scanner = new MediaScanner(context);
23328a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            compressed = scanner.extractAlbumArt(pfd.getFileDescriptor());
2333702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            pfd.close();
2334702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2335d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // If no embedded art exists, look for a suitable image file in the
23363f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // same directory as the media file, except if that directory is
23373f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen            // is the root directory of the sd card or the download directory.
2338d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // We look for, in order of preference:
2339d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 0 AlbumArt.jpg
2340d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 1 AlbumArt*Large.jpg
2341d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 2 Any other jpg image with 'albumart' anywhere in the name
2342d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 3 Any other jpg image
2343d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen            // 4 any other png image
23448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (compressed == null && path != null) {
2345702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int lastSlash = path.lastIndexOf('/');
2346702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (lastSlash > 0) {
2347d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
23483f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String artPath = path.substring(0, lastSlash);
23493f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                    String sdroot = Environment.getExternalStorageDirectory().getAbsolutePath();
23500fe3097230b324e65a874bd7c8c0f430d2fb8cbeMarco Nelissen                    String dwndir = Environment.getExternalStoragePublicDirectory(
23512f07f572bc574b685b491ee07a6209c7f2dcb13fMarco Nelissen                            Environment.DIRECTORY_DOWNLOADS).getAbsolutePath();
2352d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
2353d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    String bestmatch = null;
2354d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    synchronized (sFolderArtMap) {
2355d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (sFolderArtMap.containsKey(artPath)) {
2356d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = sFolderArtMap.get(artPath);
23573f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        } else if (!artPath.equals(sdroot) &&
23583f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                                !artPath.equals(dwndir)) {
2359d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            File dir = new File(artPath);
2360d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            String [] entrynames = dir.list();
2361d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            if (entrynames == null) {
2362d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                return null;
2363d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
2364d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            bestmatch = null;
2365d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            int matchlevel = 1000;
2366d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            for (int i = entrynames.length - 1; i >=0; i--) {
2367d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                String entry = entrynames[i].toLowerCase();
2368d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (entry.equals("albumart.jpg")) {
2369d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2370d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    break;
2371d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.startsWith("albumart")
2372d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith("large.jpg")
2373d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 1) {
2374d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2375d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 1;
2376d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.contains("albumart")
2377d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && entry.endsWith(".jpg")
2378d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                        && matchlevel > 2) {
2379d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2380d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 2;
2381d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".jpg") && matchlevel > 3) {
2382d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2383d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 3;
2384d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                } else if (entry.endsWith(".png") && matchlevel > 4) {
2385d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    bestmatch = entrynames[i];
2386d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    matchlevel = 4;
2387d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
2388d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            }
2389d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            // note that this may insert null if no album art was found
2390d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            sFolderArtMap.put(artPath, bestmatch);
2391d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        }
2392d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    }
2393d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen
2394d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                    if (bestmatch != null) {
23953f9c37ea95a3bdf5f84dc79eb199256c4deda29aMarco Nelissen                        File file = new File(artPath, bestmatch);
2396d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                        if (file.exists()) {
2397d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            compressed = new byte[(int)file.length()];
2398d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            FileInputStream stream = null;
2399d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            try {
2400d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream = new FileInputStream(file);
2401d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                stream.read(compressed);
2402d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } catch (IOException ex) {
2403d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                compressed = null;
2404d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                            } finally {
2405d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                if (stream != null) {
2406d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                    stream.close();
2407d239857cc84099d32b0439993c4e3eef2129f771Marco Nelissen                                }
2408702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            }
2409702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2410702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2411702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2412702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
24138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (IOException e) {
24148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
2415702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
24168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return compressed;
24178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
2418702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
24198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Return a URI to write the album art to and update the database as necessary.
24208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    Uri getAlbumArtOutputUri(SQLiteDatabase db, long album_id, Uri albumart_uri) {
24218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Uri out = null;
24228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // TODO: this could be done more efficiently with a call to db.replace(), which
24238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        // replaces or inserts as needed, making it unnecessary to query() first.
24248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (albumart_uri != null) {
24258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Cursor c = query(albumart_uri, new String [] { "_data" },
24268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    null, null, null);
242771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            if (c.moveToFirst()) {
24288a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                String albumart_path = c.getString(0);
24298a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (ensureFileExists(albumart_path)) {
24308a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = albumart_uri;
2431702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
243271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } else {
243371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                albumart_uri = null;
2434702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
24358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            c.close();
243671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        }
243771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (albumart_uri == null){
24388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            ContentValues initialValues = new ContentValues();
24398a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            initialValues.put("album_id", album_id);
24408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            try {
24418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                ContentValues values = ensureFile(false, initialValues, "", ALBUM_THUMB_FOLDER);
24428a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                long rowId = db.insert("album_art", "_data", values);
24438a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (rowId > 0) {
24448a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                    out = ContentUris.withAppendedId(ALBUMART_URI, rowId);
2445702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
24468a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } catch (IllegalStateException ex) {
24478a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                Log.e(TAG, "error creating album thumb file");
24488a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
24498a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
24508a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        return out;
24518a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
24528a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
24538a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // Write out the album art to the output URI, recompresses the given Bitmap
24548a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    // if necessary, otherwise writes the compressed data.
24558a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    private void writeAlbumArt(
24568a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            boolean need_to_recompress, Uri out, byte[] compressed, Bitmap bm) {
24578a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean success = false;
24588a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
24598a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            OutputStream outstream = getContext().getContentResolver().openOutputStream(out);
24608a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
24618a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (!need_to_recompress) {
24628a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // No need to recompress here, just write out the original
24638a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // compressed data here.
24648a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                outstream.write(compressed);
24658a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = true;
24668a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
24678a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                success = bm.compress(Bitmap.CompressFormat.JPEG, 75, outstream);
2468702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
24698a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
24708a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            outstream.close();
24718a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (FileNotFoundException ex) {
24728a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
2473702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } catch (IOException ex) {
24748a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            Log.e(TAG, "error creating file", ex);
24758a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
24768a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (!success) {
24778a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // the thumbnail was not written successfully, delete the entry that refers to it
24788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            getContext().getContentResolver().delete(out, null, null);
2479702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
24808a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber    }
24818a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
248271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor getThumb(SQLiteDatabase db, String path, long album_id,
248371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            Uri albumart_uri) {
248471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        ThumbData d = new ThumbData();
248571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.db = db;
248671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.path = path;
248771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.album_id = album_id;
248871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        d.albumart_uri = albumart_uri;
248971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return makeThumbInternal(d);
249071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    }
249171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen
249271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private ParcelFileDescriptor makeThumbInternal(ThumbData d) {
24938a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        byte[] compressed = getCompressedAlbumArt(getContext(), d.path);
2494702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
24958a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (compressed == null) {
249671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
24978a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
24988a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
24998a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        Bitmap bm = null;
25008a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        boolean need_to_recompress = true;
25018a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
25028a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        try {
25038a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // get the size of the bitmap
25048a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.Options opts = new BitmapFactory.Options();
25058a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inJustDecodeBounds = true;
25068a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            opts.inSampleSize = 1;
25078a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
25088a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
25098a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // request a reasonably sized output image
25108a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            // TODO: don't hardcode the size
25118a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            while (opts.outHeight > 320 || opts.outWidth > 320) {
25128a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outHeight /= 2;
25138a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.outWidth /= 2;
25148a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inSampleSize *= 2;
25158a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
25168a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
25178a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            if (opts.inSampleSize == 1) {
25188a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // The original album art was of proper size, we won't have to
25198a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // recompress the bitmap later.
25208a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                need_to_recompress = false;
25218a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            } else {
25228a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                // get the image for real now
25238a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inJustDecodeBounds = false;
25248a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                opts.inPreferredConfig = Bitmap.Config.RGB_565;
25258a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                bm = BitmapFactory.decodeByteArray(compressed, 0, compressed.length, opts);
25268a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
25278a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                if (bm != null && bm.getConfig() == null) {
2528a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    Bitmap nbm = bm.copy(Bitmap.Config.RGB_565, false);
2529a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    if (nbm != null && nbm != bm) {
2530a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm.recycle();
2531a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                        bm = nbm;
2532a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    }
25338a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber                }
25348a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber            }
25358a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        } catch (Exception e) {
25368a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
25378a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
25388a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        if (need_to_recompress && bm == null) {
253971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            return null;
25408a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
25418a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber
254271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        if (d.albumart_uri == null) {
254371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // this one doesn't need to be saved (probably a song with an unknown album),
254471ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            // so stick it in a memory file and return that
254571ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            try {
254671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                MemoryFile file = new MemoryFile("albumthumb", compressed.length);
254771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                file.writeBytes(compressed, 0, 0, compressed.length);
254871ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                file.deactivate();
254971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                return file.getParcelFileDescriptor();
255071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            } catch (IOException e) {
255171ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
255271ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        } else {
2553a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This one needs to actually be saved on the sd card.
2554a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // This is wrapped in a transaction because there are various things
2555a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // that could go wrong while generating the thumbnail, and we only want
2556a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            // to update the database when all steps succeeded.
2557a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            d.db.beginTransaction();
2558a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            try {
2559a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                Uri out = getAlbumArtOutputUri(d.db, d.album_id, d.albumart_uri);
2560a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen
2561a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (out != null) {
2562a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    writeAlbumArt(need_to_recompress, out, compressed, bm);
2563a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    getContext().getContentResolver().notifyChange(MEDIA_URI, null);
2564a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    ParcelFileDescriptor pfd = openFileHelper(out, "r");
2565a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    d.db.setTransactionSuccessful();
2566a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    return pfd;
2567a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                }
2568a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (FileNotFoundException ex) {
2569a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
2570a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } catch (UnsupportedOperationException ex) {
2571a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                // do nothing, just return null below
2572a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen            } finally {
2573a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                d.db.endTransaction();
2574a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                if (bm != null) {
2575a7953f20fc1f893712b07f9216d7b0e452eec779Marco Nelissen                    bm.recycle();
257671ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen                }
257771ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen            }
25788a4de789c3eafb17dd1eed82bc94fd9c2eb59e52Andreas Huber        }
257971ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        return null;
2580702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2581702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2582702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2583702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Look up the artist or album entry for the given name, creating that entry
2584702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * if it does not already exists.
2585702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param db        The database
2586702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param table     The table to store the key/name pair in.
2587702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param keyField  The name of the key-column
2588702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param nameField The name of the name-column
2589702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param rawName   The name that the calling app was trying to insert into the database
259059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param cacheName The string that will be inserted in to the cache
259159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param path      The full path to the file being inserted in to the audio table
259259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen     * @param albumHash A hash to distinguish between different albums of the same name
2593a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen     * @param artist    The name of the artist, if known
2594702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param cache     The cache to add this entry to
2595702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param srcuri    The Uri that prompted the call to this method, used for determining whether this is
2596702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *                  the internal or external database
2597702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return          The row ID for this artist/album, or -1 if the provided name was invalid
2598702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2599702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private long getKeyIdForName(SQLiteDatabase db, String table, String keyField, String nameField,
260059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            String rawName, String cacheName, String path, int albumHash,
2601a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            String artist, HashMap<String, Long> cache, Uri srcuri) {
2602702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        long rowId;
2603702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2604702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (rawName == null || rawName.length() == 0) {
2605702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
2606702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2607702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String k = MediaStore.Audio.keyFor(rawName);
2608702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2609702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (k == null) {
2610702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            return -1;
2611702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2612702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
261359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        boolean isAlbum = table.equals("albums");
2614e31cfb1a2c21e7ac7a646d40afbb48f49fab6907Marco Nelissen        boolean isUnknown = MediaStore.UNKNOWN_STRING.equals(rawName);
261559948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
261659948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // To distinguish same-named albums, we append a hash of the path.
261759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // Ideally we would also take things like CDDB ID in to account, so
261859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // we can group files from the same album that aren't in the same
261959948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // folder, but this is a quick and easy start that works immediately
262059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // without requiring support from the mp3, mp4 and Ogg meta data
262159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        // readers, as long as the albums are in different folders.
2622a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen        if (isAlbum) {
262359948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            k = k + albumHash;
2624a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            if (isUnknown) {
2625a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen                k = k + artist;
2626a4d451b5aecc8b2aed0bc6ea341e097697aeeeb2Marco Nelissen            }
262759948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        }
262859948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen
2629702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String [] selargs = { k };
2630702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        Cursor c = db.query(table, null, keyField + "=?", selargs, null, null, null);
2631702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2632702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        try {
2633702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            switch (c.getCount()) {
2634702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 0: {
2635702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // insert new entry into table
2636702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ContentValues otherValues = new ContentValues();
2637702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(keyField, k);
2638702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        otherValues.put(nameField, rawName);
2639702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = db.insert(table, "duration", otherValues);
264059948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen                        if (path != null && isAlbum && ! isUnknown) {
2641702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // We just inserted a new album. Now create an album art thumbnail for it.
2642a9c4e330dacb37cfffa9c00f7da83cafde4accefMarco Nelissen                            makeThumbAsync(db, path, rowId);
2643702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2644702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (rowId > 0) {
2645702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
2646702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
2647702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
2648702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2649702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2650702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2651702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                case 1: {
2652702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Use the existing entry
2653702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        c.moveToFirst();
2654702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        rowId = c.getLong(0);
2655702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2656702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // Determine whether the current rawName is better than what's
2657702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        // currently stored in the table, and update the table if it is.
2658702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String currentFancyName = c.getString(2);
2659702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        String bestName = makeBestName(rawName, currentFancyName);
2660702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        if (!bestName.equals(currentFancyName)) {
2661702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            // update the table with the new name
2662702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            ContentValues newValues = new ContentValues();
2663702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            newValues.put(nameField, bestName);
2664702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            db.update(table, newValues, "rowid="+Integer.toString((int)rowId), null);
2665702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            String volume = srcuri.toString().substring(16, 24); // extract internal/external
2666702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            Uri uri = Uri.parse("content://media/" + volume + "/audio/" + table + "/" + rowId);
2667702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                            getContext().getContentResolver().notifyChange(uri, null);
2668702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        }
2669702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2670702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2671702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                default:
2672702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    // corrupt database
2673702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    Log.e(TAG, "Multiple entries in table " + table + " for key " + k);
2674702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    rowId = -1;
2675702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    break;
2676702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2677702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } finally {
2678702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (c != null) c.close();
2679702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2680702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
268159948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen        if (cache != null && ! isUnknown) {
268259948d12c73d9132fbf3930eb93897baab1a94daMarco Nelissen            cache.put(cacheName, rowId);
2683702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2684702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return rowId;
2685702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2686702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2687702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2688702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Returns the best string to use for display, given two names.
2689702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Note that this function does not necessarily return either one
2690702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * of the provided names; it may decide to return a better alternative
2691702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * (for example, specifying the inputs "Police" and "Police, The" will
2692702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * return "The Police")
2693702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2694702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * The basic assumptions are:
2695702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - longer is better ("The police" is better than "Police")
2696702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - prefix is better ("The Police" is better than "Police, The")
2697702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * - accents are better ("Mot&ouml;rhead" is better than "Motorhead")
2698702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2699702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param one The first of the two names to consider
2700702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param two The last of the two names to consider
2701702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return The actual name to use
2702702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2703702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    String makeBestName(String one, String two) {
2704702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String name;
2705702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2706702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Longer names are usually better.
2707702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (one.length() > two.length()) {
2708702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = one;
2709702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else {
2710702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            // Names with accents are usually better, and conveniently sort later
2711702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (one.toLowerCase().compareTo(two.toLowerCase()) > 0) {
2712702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = one;
2713702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2714702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                name = two;
2715702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2716702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2717702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2718702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // Prefixes are better than postfixes.
2719702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (name.endsWith(", the") || name.endsWith(",the") ||
2720702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", an") || name.endsWith(",an") ||
2721702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name.endsWith(", a") || name.endsWith(",a")) {
2722702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            String fix = name.substring(1 + name.lastIndexOf(','));
2723702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            name = fix.trim() + " " + name.substring(0, name.lastIndexOf(','));
2724702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2725702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2726702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        // TODO: word-capitalize the resulting name
2727702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return name;
2728702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2729702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2730702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2731702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2732702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Looks up the database based on the given URI.
2733702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2734702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The requested URI
2735702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @returns the database for the given URI
2736702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2737702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private DatabaseHelper getDatabaseForUri(Uri uri) {
2738702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
2739702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (uri.getPathSegments().size() > 1) {
2740702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return mDatabases.get(uri.getPathSegments().get(0));
2741702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2742702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2743702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return null;
2744702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2745702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2746702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2747702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Attach the database for a volume (internal or external).
2748702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already attached, otherwise
2749702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * checks the volume ID and sets up the corresponding database.
2750702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2751702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param volume to attach, either {@link #INTERNAL_VOLUME} or {@link #EXTERNAL_VOLUME}.
2752702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @return the content URI of the attached volume.
2753702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2754702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Uri attachVolume(String volume) {
2755702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) {
2756702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
2757702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
2758702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2759702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2760702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
2761702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (mDatabases.get(volume) != null) {  // Already attached
2762702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                return Uri.parse("content://media/" + volume);
2763702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2764702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2765702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper db;
2766702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (INTERNAL_VOLUME.equals(volume)) {
2767702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db = new DatabaseHelper(getContext(), INTERNAL_DATABASE_NAME, true);
2768702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else if (EXTERNAL_VOLUME.equals(volume)) {
2769702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String path = Environment.getExternalStorageDirectory().getPath();
2770702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                int volumeID = FileUtils.getFatVolumeId(path);
2771702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                if (LOCAL_LOGV) Log.v(TAG, path + " volume ID: " + volumeID);
2772702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2773702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // generate database name based on volume ID
2774702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                String dbName = "external-" + Integer.toHexString(volumeID) + ".db";
2775702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                db = new DatabaseHelper(getContext(), dbName, false);
27760027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen                mVolumeId = volumeID;
2777702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } else {
2778702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                throw new IllegalArgumentException("There is no volume named " + volume);
2779702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2780702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2781702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.put(volume, db);
2782702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2783702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (!db.mInternal) {
2784702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // clean up stray album art files: delete every file not in the database
2785702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File[] files = new File(
2786702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        Environment.getExternalStorageDirectory(),
2787702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        ALBUM_THUMB_FOLDER).listFiles();
2788702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                HashSet<String> fileSet = new HashSet();
2789702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                for (int i = 0; files != null && i < files.length; i++) {
2790702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    fileSet.add(files[i].getPath());
2791702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2792702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2793702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI,
2794702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        new String[] { MediaStore.Audio.Albums.ALBUM_ART }, null, null, null);
2795702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                try {
2796702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    while (cursor != null && cursor.moveToNext()) {
2797702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                        fileSet.remove(cursor.getString(0));
2798702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    }
2799702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                } finally {
2800702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (cursor != null) cursor.close();
2801702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2802702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2803702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Iterator<String> iterator = fileSet.iterator();
2804702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                while (iterator.hasNext()) {
2805702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    String filename = iterator.next();
2806702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    if (LOCAL_LOGV) Log.v(TAG, "deleting obsolete album art " + filename);
2807702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    new File(filename).delete();
2808702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                }
2809702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2810702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2811702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2812702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Attached volume: " + volume);
2813702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        return Uri.parse("content://media/" + volume);
2814702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2815702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2816702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    /**
2817702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Detach the database for a volume (must be external).
2818702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * Does nothing if the volume is already detached, otherwise
2819702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * closes the database and sends a notification to listeners.
2820702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     *
2821702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     * @param uri The content URI of the volume, as returned by {@link #attachVolume}
2822702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project     */
2823702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private void detachVolume(Uri uri) {
2824702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) {
2825702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new SecurityException(
2826702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Opening and closing databases not allowed.");
2827702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2828702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2829702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        String volume = uri.getPathSegments().get(0);
2830702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (INTERNAL_VOLUME.equals(volume)) {
2831702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new UnsupportedOperationException(
2832702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "Deleting the internal volume is not allowed");
2833702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        } else if (!EXTERNAL_VOLUME.equals(volume)) {
2834702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            throw new IllegalArgumentException(
2835702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                    "There is no volume named " + volume);
2836702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2837702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2838702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        synchronized (mDatabases) {
2839702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            DatabaseHelper database = mDatabases.get(volume);
2840702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            if (database == null) return;
2841702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2842702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            try {
2843702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                // touch the database file to show it is most recently used
2844702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                File file = new File(database.getReadableDatabase().getPath());
2845702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                file.setLastModified(System.currentTimeMillis());
2846702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            } catch (SQLException e) {
2847702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project                Log.e(TAG, "Can't touch database file", e);
2848702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            }
2849702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2850702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            mDatabases.remove(volume);
2851702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            database.close();
2852702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        }
2853702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2854702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        getContext().getContentResolver().notifyChange(uri, null);
2855702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
2856702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
2857702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2858702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static String TAG = "MediaProvider";
2859702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final boolean LOCAL_LOGV = true;
2860044b029d9023f55e8e8861ed2f6e192f1b34f9bbRay Chen    private static final int DATABASE_VERSION = 80;
2861702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String INTERNAL_DATABASE_NAME = "internal.db";
2862702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2863702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // maximum number of cached external databases to keep
2864702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MAX_EXTERNAL_DATABASES = 3;
2865702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2866702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // Delete databases that have not been used in two months
2867702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // 60 days in milliseconds (1000 * 60 * 60 * 24 * 60)
2868702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final long OBSOLETE_DATABASE_DB = 5184000000L;
2869702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2870702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private HashMap<String, DatabaseHelper> mDatabases;
2871702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2872702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private Handler mThumbHandler;
2873702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2874702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // name of the volume currently being scanned by the media scanner (or null)
2875702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mMediaScannerVolume;
2876702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
28770027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    // current FAT volume ID
28780027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private int mVolumeId;
28790027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
2880702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String INTERNAL_VOLUME = "internal";
2881702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static final String EXTERNAL_VOLUME = "external";
2882268435e85a053ac447baed4a401ca12b3ea7e6e1Marco Nelissen    static final String ALBUM_THUMB_FOLDER = "Android/data/com.android.providers.media/albumthumbs";
2883702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2884702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    // path for writing contents of in memory temp database
2885702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private String mTempDatabasePath;
2886702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2887702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA = 1;
2888702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_MEDIA_ID = 2;
2889702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS = 3;
2890702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int IMAGES_THUMBNAILS_ID = 4;
2891702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2892702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA = 100;
2893702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID = 101;
2894702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES = 102;
2895702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_GENRES_ID = 103;
2896702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS = 104;
2897702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_MEDIA_ID_PLAYLISTS_ID = 105;
2898702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES = 106;
2899702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID = 107;
2900702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS = 108;
2901702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_GENRES_ID_MEMBERS_ID = 109;
2902702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS = 110;
2903702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID = 111;
2904702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS = 112;
2905702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_PLAYLISTS_ID_MEMBERS_ID = 113;
2906702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS = 114;
2907702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID = 115;
2908702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS = 116;
2909702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMS_ID = 117;
2910702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ARTISTS_ID_ALBUMS = 118;
2911702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART = 119;
2912702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int AUDIO_ALBUMART_ID = 120;
291371ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen    private static final int AUDIO_ALBUMART_FILE_ID = 121;
2914702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2915702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA = 200;
2916702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VIDEO_MEDIA_ID = 201;
2917b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS = 202;
2918b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final int VIDEO_THUMBNAILS_ID = 203;
2919702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2920702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES = 300;
2921702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int VOLUMES_ID = 301;
2922702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2923a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_LEGACY = 400;
2924a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_BASIC = 401;
2925a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen    private static final int AUDIO_SEARCH_FANCY = 402;
2926702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2927702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final int MEDIA_SCANNER = 500;
2928702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
29290027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen    private static final int FS_ID = 600;
29300027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
2931702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final UriMatcher URI_MATCHER =
2932702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            new UriMatcher(UriMatcher.NO_MATCH);
2933702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2934b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] ID_PROJECTION = new String[] {
2935b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        MediaStore.MediaColumns._ID
2936b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
2937b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2938702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] MIME_TYPE_PROJECTION = new String[] {
2939702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns._ID, // 0
2940702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project            MediaStore.MediaColumns.MIME_TYPE, // 1
2941702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
2942702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2943b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    private static final String[] READY_FLAG_PROJECTION = new String[] {
2944b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns._ID,
2945b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            MediaStore.MediaColumns.DATA,
2946b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen            Images.Media.MINI_THUMB_MAGIC
2947b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen    };
2948b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen
2949702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    private static final String[] EXTERNAL_DATABASE_TABLES = new String[] {
2950702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "images",
2951702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "thumbnails",
2952702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_meta",
2953702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "artists",
2954702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "albums",
2955702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_genres",
2956702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_genres_map",
2957702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_playlists",
2958702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "audio_playlists_map",
2959702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        "video",
2960702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    };
2961702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2962702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    static
2963702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    {
2964702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);
2965702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/media/#", IMAGES_MEDIA_ID);
2966702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails", IMAGES_THUMBNAILS);
2967702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/images/thumbnails/#", IMAGES_THUMBNAILS_ID);
2968702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2969702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media", AUDIO_MEDIA);
2970702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#", AUDIO_MEDIA_ID);
2971702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres", AUDIO_MEDIA_ID_GENRES);
2972702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/genres/#", AUDIO_MEDIA_ID_GENRES_ID);
2973702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists", AUDIO_MEDIA_ID_PLAYLISTS);
2974702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/media/#/playlists/#", AUDIO_MEDIA_ID_PLAYLISTS_ID);
2975702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres", AUDIO_GENRES);
2976702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#", AUDIO_GENRES_ID);
2977702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members", AUDIO_GENRES_ID_MEMBERS);
2978702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/genres/#/members/#", AUDIO_GENRES_ID_MEMBERS_ID);
2979702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists", AUDIO_PLAYLISTS);
2980702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#", AUDIO_PLAYLISTS_ID);
2981702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members", AUDIO_PLAYLISTS_ID_MEMBERS);
2982702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/playlists/#/members/#", AUDIO_PLAYLISTS_ID_MEMBERS_ID);
2983702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists", AUDIO_ARTISTS);
2984702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#", AUDIO_ARTISTS_ID);
2985702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/artists/#/albums", AUDIO_ARTISTS_ID_ALBUMS);
2986702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums", AUDIO_ALBUMS);
2987702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albums/#", AUDIO_ALBUMS_ID);
2988702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart", AUDIO_ALBUMART);
2989702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/albumart/#", AUDIO_ALBUMART_ID);
299071ece60b1465f2289851fe3fc5fa7c867ba5804dMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/media/#/albumart", AUDIO_ALBUMART_FILE_ID);
2991702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2992702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media", VIDEO_MEDIA);
2993702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/video/media/#", VIDEO_MEDIA_ID);
2994b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails", VIDEO_THUMBNAILS);
2995b386112a8c5e5bd2d3d77e5398bea26b2172dffcRay Chen        URI_MATCHER.addURI("media", "*/video/thumbnails/#", VIDEO_THUMBNAILS_ID);
2996702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
2997702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/media_scanner", MEDIA_SCANNER);
2998702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
29990027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen        URI_MATCHER.addURI("media", "*/fs_id", FS_ID);
30000027019c6190f6bfa6935904107f23c8e75b1ffdMarco Nelissen
3001702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*", VOLUMES_ID);
3002702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", null, VOLUMES);
3003702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project
3004a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        /**
3005a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         * @deprecated use the 'basic' or 'fancy' search Uris instead
3006a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen         */
3007702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY,
3008a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
3009702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project        URI_MATCHER.addURI("media", "*/audio/" + SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
3010a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_LEGACY);
3011a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
3012a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used for search suggestions
3013a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY,
3014a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                AUDIO_SEARCH_BASIC);
3015a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/" + SearchManager.SUGGEST_URI_PATH_QUERY +
3016a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen                "/*", AUDIO_SEARCH_BASIC);
3017a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen
3018a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        // used by the music app's search activity
3019a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy", AUDIO_SEARCH_FANCY);
3020a65c5443d17d497703272b7ae2f0aa5b165bff3fMarco Nelissen        URI_MATCHER.addURI("media", "*/audio/search/fancy/*", AUDIO_SEARCH_FANCY);
3021702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project    }
3022702152725052b7b3903ed647cf53f04724886a1bThe Android Open Source Project}
3023