MediaScanner.java revision 08f70fa5b82faa00d4d536b5a220d7ce8dd8f932
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.media;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
19bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport org.xml.sax.Attributes;
20bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport org.xml.sax.ContentHandler;
21bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport org.xml.sax.SAXException;
22bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissen
23bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport android.content.ContentUris;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.ContentValues;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.IContentProvider;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.Cursor;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.SQLException;
295d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wangimport android.drm.DrmManagerClient;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.BitmapFactory;
310cd0136d440cf6ad9d5fab430269116786e671ecMike Lockwoodimport android.mtp.MtpConstants;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.Uri;
33250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwoodimport android.os.Environment;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Process;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.RemoteException;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.SystemProperties;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore;
38ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissenimport android.provider.MediaStore.Files.FileColumns;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.Settings;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Audio;
41c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwoodimport android.provider.MediaStore.Files;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Images;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Video;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.provider.MediaStore.Audio.Playlists;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.Element;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.ElementListener;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.sax.RootElement;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.TextUtils;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Xml;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
52bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.BufferedReader;
53bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.File;
54bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.FileDescriptor;
55bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.FileInputStream;
56bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.IOException;
57bfcb22a207499c7ecd4aef9bea2ac778d8be8fc8Marco Nelissenimport java.io.InputStreamReader;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashMap;
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashSet;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Iterator;
62d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissenimport java.util.LinkedHashMap;
63ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissenimport java.util.Locale;
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissenimport libcore.io.ErrnoException;
6658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissenimport libcore.io.Libcore;
6758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
6977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Internal service helper that no-one should use directly.
7020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync *
7177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * The way the scan currently works is:
7277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - The Java MediaScannerService creates a MediaScanner (this class), and calls
7377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   MediaScanner.scanDirectories on it.
7477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - scanDirectories() calls the native processDirectory() for each of the specified directories.
7577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - the processDirectory() JNI method wraps the provided mediascanner client in a native
7677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   'MyMediaScannerClient' class, then calls processDirectory() on the native MediaScanner
7777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   object (which got created when the Java MediaScanner was created).
787ae82ce1fd54103bb87b8a0593a4228c94b1d6f9James Dong * - native MediaScanner.processDirectory() calls
7977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   doProcessDirectory(), which recurses over the folder, and calls
8077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   native MyMediaScannerClient.scanFile() for every file whose extension matches.
8177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - native MyMediaScannerClient.scanFile() calls back on Java MediaScannerClient.scanFile,
8277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   which calls doScanFile, which after some setup calls back down to native code, calling
8377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   MediaScanner.processFile().
8477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - MediaScanner.processFile() calls one of several methods, depending on the type of the
8577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   file: parseMP3, parseMP4, parseMidi, parseOgg or parseWMA.
8677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - each of these methods gets metadata key/value pairs from the file, and repeatedly
8777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   calls native MyMediaScannerClient.handleStringTag, which calls back up to its Java
8877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   counterparts in this file.
8977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - Java handleStringTag() gathers the key/value pairs that it's interested in.
9077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * - once processFile returns and we're back in Java code in doScanFile(), it calls
9177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   Java MyMediaScannerClient.endFile(), which takes all the data that's been
9277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *   gathered and inserts an entry in to the database.
9377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen *
9477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * In summary:
9577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScannerService calls
9677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner scanDirectories, which calls
9777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner processDirectory (native method), which calls
9877f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MediaScanner processDirectory, which calls
9977f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MyMediaScannerClient scanFile, which calls
10077f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MyMediaScannerClient scanFile, which calls
10177f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScannerClient doScanFile, which calls
10277f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MediaScanner processFile (native method), which calls
10377f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MediaScanner processFile, which calls
10477f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native parseMP3, parseMP4, parseMidi, parseOgg or parseWMA, which calls
10577f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * native MyMediaScanner handleStringTag, which calls
10677f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Java MyMediaScanner handleStringTag.
10777f2c4c1f9d1b8b9266a18e8e28fcabdf6599399Marco Nelissen * Once MediaScanner processFile returns, an entry is inserted in to the database.
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109af16d0b30e89f31b7617424c8706553ff571d08dMike Lockwood * The MediaScanner class is not thread-safe, so it should only be used in a single threaded manner.
110af16d0b30e89f31b7617424c8706553ff571d08dMike Lockwood *
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide}
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class MediaScanner
11420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync{
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static {
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        System.loadLibrary("media_jni");
1174935d05eaa306cef88cf0ab13eca386f270409ecMarco Nelissen        native_init();
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private final static String TAG = "MediaScanner";
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
122d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final String[] FILES_PRESCAN_PROJECTION = new String[] {
123c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Files.FileColumns._ID, // 0
124c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Files.FileColumns.DATA, // 1
125d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            Files.FileColumns.FORMAT, // 2
126d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            Files.FileColumns.DATE_MODIFIED, // 3
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
12820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1296458935331ccfacb4cccc50d6b893947febae687Mike Lockwood    private static final String[] ID_PROJECTION = new String[] {
1306458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            Files.FileColumns._ID,
1316458935331ccfacb4cccc50d6b893947febae687Mike Lockwood    };
1326458935331ccfacb4cccc50d6b893947febae687Mike Lockwood
133d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_ID_COLUMN_INDEX = 0;
134d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_PATH_COLUMN_INDEX = 1;
135d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_FORMAT_COLUMN_INDEX = 2;
136d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood    private static final int FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX = 3;
137d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String[] PLAYLIST_MEMBERS_PROJECTION = new String[] {
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Audio.Playlists.Members.PLAYLIST_ID, // 0
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     };
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int ID_PLAYLISTS_COLUMN_INDEX = 0;
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int PATH_PLAYLISTS_COLUMN_INDEX = 1;
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int DATE_MODIFIED_PLAYLISTS_COLUMN_INDEX = 2;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String RINGTONES_DIR = "/ringtones/";
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String NOTIFICATIONS_DIR = "/notifications/";
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String ALARMS_DIR = "/alarms/";
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String MUSIC_DIR = "/music/";
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String PODCAST_DIR = "/podcasts/";
15120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String[] ID3_GENRES = {
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // ID3v1 Genres
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Blues",
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Classic Rock",
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Country",
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Dance",
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Disco",
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Funk",
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Grunge",
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Hip-Hop",
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jazz",
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Metal",
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "New Age",
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Oldies",
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Other",
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop",
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "R&B",
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rap",
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Reggae",
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rock",
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Techno",
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Industrial",
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Alternative",
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ska",
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Death Metal",
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pranks",
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Soundtrack",
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Euro-Techno",
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ambient",
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trip-Hop",
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Vocal",
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jazz+Funk",
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Fusion",
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trance",
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Classical",
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental",
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid",
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "House",
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Game",
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Sound Clip",
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gospel",
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Noise",
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "AlternRock",
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bass",
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Soul",
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Punk",
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Space",
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Meditative",
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental Pop",
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Instrumental Rock",
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ethnic",
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gothic",
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Darkwave",
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Techno-Industrial",
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Electronic",
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop-Folk",
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Eurodance",
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Dream",
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Southern Rock",
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Comedy",
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Cult",
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gangsta",
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Top 40",
2159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Christian Rap",
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Pop/Funk",
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Jungle",
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Native American",
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Cabaret",
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "New Wave",
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Psychadelic",
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rave",
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Showtunes",
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Trailer",
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Lo-Fi",
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Tribal",
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid Punk",
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acid Jazz",
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Polka",
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Retro",
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Musical",
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rock & Roll",
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Hard Rock",
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // The following genres are Winamp extensions
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folk",
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folk-Rock",
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "National Folk",
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Swing",
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Fast Fusion",
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bebob",
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Latin",
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Revival",
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Celtic",
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Bluegrass",
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Avantgarde",
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Gothic Rock",
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Progressive Rock",
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Psychedelic Rock",
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Symphonic Rock",
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Slow Rock",
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Big Band",
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chorus",
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Easy Listening",
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Acoustic",
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Humour",
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Speech",
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chanson",
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Opera",
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Chamber Music",
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Sonata",
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Symphony",
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Booty Bass",
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Primus",
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Porn Groove",
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Satire",
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Slow Jam",
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Club",
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Tango",
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Samba",
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Folklore",
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Ballad",
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Power Ballad",
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Rhythmic Soul",
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Freestyle",
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Duet",
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Punk Rock",
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Drum Solo",
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "A capella",
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        "Euro-House",
280cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Dance Hall",
281cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        // The following ones seem to be fairly widely supported as well
282cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Goa",
283cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Drum & Bass",
284cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Club-House",
285cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Hardcore",
286cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Terror",
287cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Indie",
288cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Britpop",
289cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Negerpunk",
290cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Polsk Punk",
291cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Beat",
292cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Christian Gangsta",
293cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Heavy Metal",
294cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Black Metal",
295cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Crossover",
296cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Contemporary Christian",
297cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Christian Rock",
298cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Merengue",
299cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Salsa",
300cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Thrash Metal",
301cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Anime",
302cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "JPop",
303cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        "Synthpop",
304cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        // 148 and up don't seem to have been defined yet.
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    };
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mNativeContext;
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Context mContext;
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private IContentProvider mMediaProvider;
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mAudioUri;
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mVideoUri;
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mImagesUri;
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mThumbsUri;
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private Uri mPlaylistsUri;
315c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    private Uri mFilesUri;
31643b076bb75e86fbb34cbe36aa0298c7a647121ffMike Lockwood    private boolean mProcessPlaylists, mProcessGenres;
317d815f79766984fce499e147ecbacc01914683f74Mike Lockwood    private int mMtpObjectHandle;
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
319250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood    private final String mExternalStoragePath;
320250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood
321d6b3e0cc36cbe28c8d194b63227df37c76af7efaMarco Nelissen    /** whether to use bulk inserts or individual inserts for each item */
322d6b3e0cc36cbe28c8d194b63227df37c76af7efaMarco Nelissen    private static final boolean ENABLE_BULK_INSERTS = true;
3233410b4656505673c72da558dcfec94d7b135e760Mike Lockwood
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // used when scanning the image database so we know whether we have to prune
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // old thumbnail files
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int mOriginalCount;
327bcbddd9342ee5ef48617ce7dcca6c505df3fcf08Marco Nelissen    /** Whether the database had any entries in it before the scan started */
328bcbddd9342ee5ef48617ce7dcca6c505df3fcf08Marco Nelissen    private boolean mWasEmptyPriorToScan = false;
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Whether the scanner has set a default sound for the ringer ringtone. */
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mDefaultRingtoneSet;
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** Whether the scanner has set a default sound for the notification ringtone. */
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mDefaultNotificationSet;
3333156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    /** Whether the scanner has set a default sound for the alarm ringtone. */
3343156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    private boolean mDefaultAlarmSet;
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** The filename for the default sound for the ringer ringtone. */
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mDefaultRingtoneFilename;
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /** The filename for the default sound for the notification ringtone. */
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private String mDefaultNotificationFilename;
3393156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    /** The filename for the default sound for the alarm ringtone. */
3403156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott    private String mDefaultAlarmAlertFilename;
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The prefix for system properties that define the default sound for
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * ringtones. Concatenate the name of the setting from Settings
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to get the full system property.
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String DEFAULT_RINGTONE_PROPERTY_PREFIX = "ro.config.";
34720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // set to true if file path comparisons should be case insensitive.
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // this should be set when scanning files on a case insensitive file system.
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean mCaseInsensitivePaths;
35120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
35230c918ce7fbe171944b28fc91b3f22b3d631872dGlenn Kasten    private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
35458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen    private static class FileEntry {
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long mRowId;
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String mPath;
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long mLastModified;
358d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood        int mFormat;
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean mLastModifiedChanged;
36020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
36158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        FileEntry(long rowId, String path, long lastModified, int format) {
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mRowId = rowId;
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPath = path;
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModified = lastModified;
365d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood            mFormat = format;
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModifiedChanged = false;
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        @Override
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String toString() {
3713e5e8055942ee8179675b8ed11d40ca12dbe42e6Marco Nelissen            return mPath + " mRowId: " + mRowId;
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
37420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3758e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang    private MediaInserter mMediaInserter;
376bd682b040833fce9e212c00c395b32bec7050f87Mike Lockwood
37758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen    private ArrayList<FileEntry> mPlayLists;
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3795d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang    private DrmManagerClient mDrmManagerClient = null;
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public MediaScanner(Context c) {
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        native_setup();
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext = c;
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBitmapOptions.inSampleSize = 1;
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBitmapOptions.inJustDecodeBounds = true;
38620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        setDefaultRingtoneFileNames();
388250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood
389250528e5b2362cb7b6749afb331acd8226f67744Mike Lockwood        mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
390ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        //mClient.testGenreNameConverter();
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void setDefaultRingtoneFileNames() {
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultRingtoneFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + Settings.System.RINGTONE);
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDefaultNotificationFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                + Settings.System.NOTIFICATION_SOUND);
3983156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott        mDefaultAlarmAlertFilename = SystemProperties.get(DEFAULT_RINGTONE_PROPERTY_PREFIX
3993156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                + Settings.System.ALARM_ALERT);
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
40120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
40230c918ce7fbe171944b28fc91b3f22b3d631872dGlenn Kasten    private final MyMediaScannerClient mClient = new MyMediaScannerClient();
40320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
404e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen    private boolean isDrmEnabled() {
4051f89c4f9f8df5d7125221250993ba1e853ed9fedGloria Wang        String prop = SystemProperties.get("drm.service.enabled");
406e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen        return prop != null && prop.equals("true");
407e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen    }
408fbc4939d2b51d511858846363bf02c7c9f851ed2Mike Lockwood
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private class MyMediaScannerClient implements MediaScannerClient {
41020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mArtist;
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mAlbumArtist;    // use this if mArtist is missing
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mAlbum;
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mTitle;
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mComposer;
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mGenre;
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mMimeType;
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mFileType;
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mTrack;
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mYear;
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int mDuration;
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private String mPath;
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private long mLastModified;
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private long mFileSize;
425c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva        private String mWriter;
426f679ba7d944a403fbdc77926c44978ffd5161f2dMarco Nelissen        private int mCompilation;
42782428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang        private boolean mIsDrm;
428997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        private boolean mNoMedia;   // flag to suppress file from appearing in media tables
429e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang        private int mWidth;
430e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang        private int mHeight;
43120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
43258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        public FileEntry beginFile(String path, String mimeType, long lastModified,
433997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                long fileSize, boolean isDirectory, boolean noMedia) {
434076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood            mMimeType = mimeType;
435076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood            mFileType = 0;
436076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood            mFileSize = fileSize;
43720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
438076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood            if (!isDirectory) {
439ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                if (!noMedia && isNoMediaFile(path)) {
440ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                    noMedia = true;
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
442997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                mNoMedia = noMedia;
44320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
444076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                // try mimeType first, if it is specified
445076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                if (mimeType != null) {
446076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                    mFileType = MediaFile.getFileTypeForMimeType(mimeType);
447076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                }
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
449076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                // if mimeType was not specified, compute file type based on file extension.
450076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                if (mFileType == 0) {
451076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                    MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
452076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                    if (mediaFileType != null) {
453076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                        mFileType = mediaFileType.fileType;
454076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                        if (mMimeType == null) {
455076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                            mMimeType = mediaFileType.mimeType;
456076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                        }
457858caeaaa1db4076565e6df06b2b2721ec453c4eMike Lockwood                    }
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
45920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
460076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                if (isDrmEnabled() && MediaFile.isDrmFileType(mFileType)) {
461076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                    mFileType = getFileTypeFromDrm(path);
462076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                }
4635d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
4645d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
46558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            FileEntry entry = makeEntryFor(path);
466a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen            // add some slack to avoid a rounding error
467a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen            long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0;
468a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen            boolean wasModified = delta > 1 || delta < -1;
469a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen            if (entry == null || wasModified) {
470a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                if (wasModified) {
471a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                    entry.mLastModified = lastModified;
472a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                } else {
47358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    entry = new FileEntry(0, path, lastModified,
474a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                            (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0));
475a8a656b0c595fa876a387a91f8813f88a2d3abe7Marco Nelissen                }
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                entry.mLastModifiedChanged = true;
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
47820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) {
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mPlayLists.add(entry);
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we don't process playlists in the main scan, so return null
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
48420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // clear all the metadata
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mArtist = null;
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAlbumArtist = null;
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mAlbum = null;
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTitle = null;
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mComposer = null;
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mGenre = null;
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTrack = 0;
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mYear = 0;
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDuration = 0;
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPath = path;
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mLastModified = lastModified;
497c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva            mWriter = null;
498f679ba7d944a403fbdc77926c44978ffd5161f2dMarco Nelissen            mCompilation = 0;
49982428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang            mIsDrm = false;
500e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            mWidth = 0;
501e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            mHeight = 0;
50220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return entry;
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
50520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
506bcbddd9342ee5ef48617ce7dcca6c505df3fcf08Marco Nelissen        @Override
507997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        public void scanFile(String path, long lastModified, long fileSize,
508997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                boolean isDirectory, boolean noMedia) {
50900c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            // This is the callback funtion from native codes.
51000c575a3fccb9d3065e913f1b8fcf93e18d44eafRay Chen            // Log.v(TAG, "scanFile: "+path);
511997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            doScanFile(path, null, lastModified, fileSize, isDirectory, false, noMedia);
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
514c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        public Uri doScanFile(String path, String mimeType, long lastModified,
515997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                long fileSize, boolean isDirectory, boolean scanAlways, boolean noMedia) {
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Uri result = null;
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            long t1 = System.currentTimeMillis();
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
51958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                FileEntry entry = beginFile(path, mimeType, lastModified,
520997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        fileSize, isDirectory, noMedia);
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // rescan for metadata if file was modified since last scan
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
523997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    if (noMedia) {
524997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        result = endFile(entry, false, false, false, false, false);
525997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    } else {
526997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        String lowpath = path.toLowerCase();
527997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        boolean ringtones = (lowpath.indexOf(RINGTONES_DIR) > 0);
528997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        boolean notifications = (lowpath.indexOf(NOTIFICATIONS_DIR) > 0);
529997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        boolean alarms = (lowpath.indexOf(ALARMS_DIR) > 0);
530997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        boolean podcasts = (lowpath.indexOf(PODCAST_DIR) > 0);
531997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||
532997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            (!ringtones && !notifications && !alarms && !podcasts);
533997354e4b4a9666cedd62282471e97822affced9Mike Lockwood
534997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        // we only extract metadata for audio and video files
535997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        if (MediaFile.isAudioFileType(mFileType)
536997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                                || MediaFile.isVideoFileType(mFileType)) {
537997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            processFile(path, mimeType, this);
538997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        }
53920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
540e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                        if (MediaFile.isImageFileType(mFileType)) {
541e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                            processImageFile(path);
542e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                        }
543e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang
544997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        result = endFile(entry, ringtones, notifications, alarms, music, podcasts);
545997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    }
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (RemoteException e) {
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            long t2 = System.currentTimeMillis();
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project//            Log.v(TAG, "scanFile: " + path + " took " + (t2-t1));
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private int parseSubstring(String s, int start, int defaultValue) {
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int length = s.length();
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start == length) return defaultValue;
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            char ch = s.charAt(start++);
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // return defaultValue if we have no integer at all
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ch < '0' || ch > '9') return defaultValue;
56220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int result = ch - '0';
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            while (start < length) {
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ch = s.charAt(start++);
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (ch < '0' || ch > '9') return result;
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result = result * 10 + (ch - '0');
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
56920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
57120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        }
57220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void handleStringTag(String name, String value) {
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (name.equalsIgnoreCase("title") || name.startsWith("title;")) {
57573bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // Don't trim() here, to preserve the special \001 character
57673bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // used to force sorting. The media provider will trim() before
57773bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                // inserting the title in to the database.
57873bb511adee5444ecd041146fbbd3677fb635949Marco Nelissen                mTitle = value;
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("artist") || name.startsWith("artist;")) {
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mArtist = value.trim();
581ed8188711a794867b47d21bbed3e26827f6c27e3Neel Parekh            } else if (name.equalsIgnoreCase("albumartist") || name.startsWith("albumartist;")
582ed8188711a794867b47d21bbed3e26827f6c27e3Neel Parekh                    || name.equalsIgnoreCase("band") || name.startsWith("band;")) {
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAlbumArtist = value.trim();
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("album") || name.startsWith("album;")) {
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mAlbum = value.trim();
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("composer") || name.startsWith("composer;")) {
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mComposer = value.trim();
58843b076bb75e86fbb34cbe36aa0298c7a647121ffMike Lockwood            } else if (mProcessGenres &&
58943b076bb75e86fbb34cbe36aa0298c7a647121ffMike Lockwood                    (name.equalsIgnoreCase("genre") || name.startsWith("genre;"))) {
590cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                mGenre = getGenreName(value);
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("year") || name.startsWith("year;")) {
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mYear = parseSubstring(value, 0, 0);
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("tracknumber") || name.startsWith("tracknumber;")) {
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // track number might be of the form "2/12"
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we just read the number before the slash
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int num = parseSubstring(value, 0, 0);
59720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                mTrack = (mTrack / 1000) * 1000 + num;
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("discnumber") ||
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    name.equals("set") || name.startsWith("set;")) {
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // set number might be of the form "1/3"
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // we just read the number before the slash
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int num = parseSubstring(value, 0, 0);
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mTrack = (num * 1000) + (mTrack % 1000);
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (name.equalsIgnoreCase("duration")) {
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mDuration = parseSubstring(value, 0, 0);
606c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva            } else if (name.equalsIgnoreCase("writer") || name.startsWith("writer;")) {
607c9706f0262a2f399f95e0d78ba2b7616e08d0069Sahil Sachdeva                mWriter = value.trim();
608f679ba7d944a403fbdc77926c44978ffd5161f2dMarco Nelissen            } else if (name.equalsIgnoreCase("compilation")) {
609f679ba7d944a403fbdc77926c44978ffd5161f2dMarco Nelissen                mCompilation = parseSubstring(value, 0, 0);
61082428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang            } else if (name.equalsIgnoreCase("isdrm")) {
61182428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang                mIsDrm = (parseSubstring(value, 0, 0) == 1);
612e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen            } else if (name.equalsIgnoreCase("width")) {
613e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                mWidth = parseSubstring(value, 0, 0);
614e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen            } else if (name.equalsIgnoreCase("height")) {
615e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                mHeight = parseSubstring(value, 0, 0);
616ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            } else {
617ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                //Log.v(TAG, "unknown tag: " + name + " (" + mProcessGenres + ")");
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
62020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
621ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        private boolean convertGenreCode(String input, String expected) {
622ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            String output = getGenreName(input);
623ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            if (output.equals(expected)) {
624ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                return true;
625ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            } else {
626ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                Log.d(TAG, "'" + input + "' -> '" + output + "', expected '" + expected + "'");
627ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                return false;
628ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            }
629ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        }
630ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        private void testGenreNameConverter() {
631ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("2", "Country");
632ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2)", "Country");
633ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2", "(2");
634ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("2 Foo", "Country");
635ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2) Foo", "Country");
636ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2 Foo", "(2 Foo");
637ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("2Foo", "2Foo");
638ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(2)Foo", "Country");
639ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("200 Foo", "Foo");
640ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(200) Foo", "Foo");
641ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("200Foo", "200Foo");
642ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("(200)Foo", "Foo");
643ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("200)Foo", "200)Foo");
644ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            convertGenreCode("200) Foo", "200) Foo");
645ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen        }
646ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen
647cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        public String getGenreName(String genreTagValue) {
648cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen
649cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            if (genreTagValue == null) {
650cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                return null;
651cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            }
652cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            final int length = genreTagValue.length();
653cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen
654ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen            if (length > 0) {
655ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                boolean parenthesized = false;
656cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                StringBuffer number = new StringBuffer();
657ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                int i = 0;
658ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                for (; i < length; ++i) {
659cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    char c = genreTagValue.charAt(i);
660ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                    if (i == 0 && c == '(') {
661ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                        parenthesized = true;
662ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                    } else if (Character.isDigit(c)) {
663cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        number.append(c);
664cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    } else {
665cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        break;
666cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    }
667cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                }
668ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                char charAfterNumber = i < length ? genreTagValue.charAt(i) : ' ';
669ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                if ((parenthesized && charAfterNumber == ')')
670ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                        || !parenthesized && Character.isWhitespace(charAfterNumber)) {
671cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    try {
672cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        short genreIndex = Short.parseShort(number.toString());
673cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        if (genreIndex >= 0) {
674cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            if (genreIndex < ID3_GENRES.length) {
675cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                return ID3_GENRES[genreIndex];
676cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            } else if (genreIndex == 0xFF) {
677cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                return null;
678cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            } else if (genreIndex < 0xFF && (i + 1) < length) {
679cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                // genre is valid but unknown,
680cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                // if there is a string after the value we take it
681ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                if (parenthesized && charAfterNumber == ')') {
682ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                    i++;
683ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                }
684ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                String ret = genreTagValue.substring(i).trim();
685ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                if (ret.length() != 0) {
686ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                    return ret;
687ad35f9165e7f2641812a5069de9c3cd3fdd16923Marco Nelissen                                }
688cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            } else {
689cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                // else return the number, without parentheses
690cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                                return number.toString();
691cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                            }
692cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                        }
693cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    } catch (NumberFormatException e) {
694cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                    }
695cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen                }
696cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            }
697cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen
698cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen            return genreTagValue;
699cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen        }
700cb613a04c16915f6374c6436b5b37355bce3ed1cMarco Nelissen
701e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang        private void processImageFile(String path) {
702e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            try {
703e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                mBitmapOptions.outWidth = 0;
704e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                mBitmapOptions.outHeight = 0;
705e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                BitmapFactory.decodeFile(path, mBitmapOptions);
706e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                mWidth = mBitmapOptions.outWidth;
707e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                mHeight = mBitmapOptions.outHeight;
708e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            } catch (Throwable th) {
709e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                // ignore;
710e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            }
711e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang        }
712e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setMimeType(String mimeType) {
7141851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen            if ("audio/mp4".equals(mMimeType) &&
7151851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                    mimeType.startsWith("video")) {
7161851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // for feature parity with Donut, we force m4a files to keep the
7171851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // audio/mp4 mimetype, even if they are really "enhanced podcasts"
7181851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                // with a video track
7191851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen                return;
7201851db6804cbdf8d2c8ede0725e73be6fda0f3c0Marco Nelissen            }
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMimeType = mimeType;
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mFileType = MediaFile.getFileTypeForMimeType(mimeType);
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
72420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Formats the data into a values array suitable for use with the Media
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * Content Provider.
72820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync         *
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return a map of values
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private ContentValues toValues() {
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ContentValues map = new ContentValues();
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.DATA, mPath);
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.TITLE, mTitle);
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.DATE_MODIFIED, mLastModified);
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.SIZE, mFileSize);
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            map.put(MediaStore.MediaColumns.MIME_TYPE, mMimeType);
73982428a862f325238cfb5646bbd65de3f1a11e7ccGloria Wang            map.put(MediaStore.MediaColumns.IS_DRM, mIsDrm);
74020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
741e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen            String resolution = null;
742e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            if (mWidth > 0 && mHeight > 0) {
743e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                map.put(MediaStore.MediaColumns.WIDTH, mWidth);
744e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang                map.put(MediaStore.MediaColumns.HEIGHT, mHeight);
745e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                resolution = mWidth + "x" + mHeight;
746e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang            }
747e1bf8efaff42dc33b7a4663f3c9d50d12de81bcbChih-Chung Chang
748e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood            if (!mNoMedia) {
749997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                if (MediaFile.isVideoFileType(mFileType)) {
750997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0
751997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            ? mArtist : MediaStore.UNKNOWN_STRING));
752997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0
753997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            ? mAlbum : MediaStore.UNKNOWN_STRING));
754997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Video.Media.DURATION, mDuration);
755e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                    if (resolution != null) {
756e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                        map.put(Video.Media.RESOLUTION, resolution);
757e313f721ad97ad44666dd066a891acb4ddfa8ab3Marco Nelissen                    }
758997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                } else if (MediaFile.isImageFileType(mFileType)) {
759997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    // FIXME - add DESCRIPTION
760997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                } else if (MediaFile.isAudioFileType(mFileType)) {
761997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ?
762997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            mArtist : MediaStore.UNKNOWN_STRING);
763997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null &&
764997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            mAlbumArtist.length() > 0) ? mAlbumArtist : null);
765997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ?
766997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                            mAlbum : MediaStore.UNKNOWN_STRING);
767997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.COMPOSER, mComposer);
768e5d0495d5206167c597efcd81436a0f881f069f9Mike Lockwood                    map.put(Audio.Media.GENRE, mGenre);
769997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    if (mYear != 0) {
770997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                        map.put(Audio.Media.YEAR, mYear);
771997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    }
772997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.TRACK, mTrack);
773997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.DURATION, mDuration);
774997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    map.put(Audio.Media.COMPILATION, mCompilation);
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return map;
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
77920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
78058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
78120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                boolean alarms, boolean music, boolean podcasts)
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                throws RemoteException {
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // update database
78420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
785997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            // use album artist if artist is missing
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (mArtist == null || mArtist.length() == 0) {
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mArtist = mAlbumArtist;
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ContentValues values = toValues();
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String title = values.getAsString(MediaStore.MediaColumns.TITLE);
792bdc0e82f9c075edb67da4a2583e557824c280415Mike Lockwood            if (title == null || TextUtils.isEmpty(title.trim())) {
793c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                title = MediaFile.getFileTitle(values.getAsString(MediaStore.MediaColumns.DATA));
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(MediaStore.MediaColumns.TITLE, title);
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
796c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen            String album = values.getAsString(Audio.Media.ALBUM);
7979a488b44cb479bc42375d2cfcdd4aa6161dc17cfMarco Nelissen            if (MediaStore.UNKNOWN_STRING.equals(album)) {
798c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                album = values.getAsString(MediaStore.MediaColumns.DATA);
799c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                // extract last path segment before file name
800c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                int lastSlash = album.lastIndexOf('/');
801c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                if (lastSlash >= 0) {
802c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    int previousSlash = 0;
803c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    while (true) {
804c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        int idx = album.indexOf('/', previousSlash + 1);
805c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        if (idx < 0 || idx >= lastSlash) {
806c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                            break;
807c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        }
808c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        previousSlash = idx;
809c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    }
810c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    if (previousSlash != 0) {
811c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        album = album.substring(previousSlash + 1, lastSlash);
812c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                        values.put(Audio.Media.ALBUM, album);
813c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                    }
814c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen                }
815c3c08ba5d67d836274fabdf4c1bff6f1053d95a3Marco Nelissen            }
816c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen            long rowId = entry.mRowId;
817c2206a0710a3ca2c29854c1a0638c5ba55629ba2Mike Lockwood            if (MediaFile.isAudioFileType(mFileType) && (rowId == 0 || mMtpObjectHandle != 0)) {
818c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // Only set these for new entries. For existing entries, they
819c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // may have been modified later, and we want to keep the current
820c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // values so that custom ringtones still show up in the ringtone
821c066557378c2305625a7bad3d25f084ad647ef27Marco Nelissen                // picker.
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_RINGTONE, ringtones);
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_NOTIFICATION, notifications);
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_ALARM, alarms);
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_MUSIC, music);
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.put(Audio.Media.IS_PODCAST, podcasts);
827997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            } else if (mFileType == MediaFile.FILE_TYPE_JPEG && !mNoMedia) {
828700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                ExifInterface exif = null;
829700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                try {
830700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                    exif = new ExifInterface(entry.mPath);
831700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                } catch (IOException ex) {
832700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                    // exif is null
833700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                }
834700beb484624a9a34649cb6ff088468e78b758ffChih-Chung Chang                if (exif != null) {
835872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang                    float[] latlng = new float[2];
836872a30ec723ebdd97de764406544516545d7c9d4Chih-Chung Chang                    if (exif.getLatLong(latlng)) {
83720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                        values.put(Images.Media.LATITUDE, latlng[0]);
83820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                        values.put(Images.Media.LONGITUDE, latlng[1]);
83920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    }
840099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang
8413a8cab88e7747dc280ce85895af014f98e80a6ccRay Chen                    long time = exif.getGpsDateTime();
842099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang                    if (time != -1) {
843052d9c8b414dc9eb1c70d849660592d9bc3b2644Ray Chen                        values.put(Images.Media.DATE_TAKEN, time);
844a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                    } else {
845a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        // If no time zone information is available, we should consider using
846a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        // EXIF local time as taken time if the difference between file time
847a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        // and EXIF local time is not less than 1 Day, otherwise MediaProvider
848a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        // will use file time as taken time.
849a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        time = exif.getDateTime();
850872e7b7073fad8295e6314c73b87d7cb3f85d39cRay Chen                        if (time != -1 && Math.abs(mLastModified * 1000 - time) >= 86400000) {
851a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                            values.put(Images.Media.DATE_TAKEN, time);
852a2c322f599b5a44ca153922b994a05a4311331cbRay Chen                        }
853099397cbd07c8c991f3126d0d0ac64bb6b3c0b47Chih-Chung Chang                    }
854b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang
855b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    int orientation = exif.getAttributeInt(
856b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        ExifInterface.TAG_ORIENTATION, -1);
857b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    if (orientation != -1) {
858b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        // We only recognize a subset of orientation tag values.
859b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        int degree;
860b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        switch(orientation) {
861b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_90:
862b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 90;
863b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
864b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_180:
865b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 180;
866b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
867b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            case ExifInterface.ORIENTATION_ROTATE_270:
868b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 270;
869b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
870b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                            default:
871b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                degree = 0;
872b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                                break;
873b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        }
874b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                        values.put(Images.Media.ORIENTATION, degree);
875b8f4725d1bb750e3358a5b5dfcec7b1c5d3b9939Chih-Chung Chang                    }
87620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                }
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
87820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
87945aaa2a345292bb6d600822db602f25e261c0e50Mike Lockwood            Uri tableUri = mFilesUri;
8808e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang            MediaInserter inserter = mMediaInserter;
881e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood            if (!mNoMedia) {
882e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                if (MediaFile.isVideoFileType(mFileType)) {
883e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    tableUri = mVideoUri;
884e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                } else if (MediaFile.isImageFileType(mFileType)) {
885e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    tableUri = mImagesUri;
886e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                } else if (MediaFile.isAudioFileType(mFileType)) {
887e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    tableUri = mAudioUri;
888e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                }
889e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood            }
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Uri result = null;
8916c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim            boolean needToSetSettings = false;
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (rowId == 0) {
893c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                if (mMtpObjectHandle != 0) {
894c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                    values.put(MediaStore.MediaColumns.MEDIA_SCANNER_NEW_OBJECT_ID, mMtpObjectHandle);
895c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                }
896e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                if (tableUri == mFilesUri) {
897e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    int format = entry.mFormat;
898e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    if (format == 0) {
899e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                        format = MediaFile.getFormatCode(entry.mPath, mMimeType);
900e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    }
901e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood                    values.put(Files.FileColumns.FORMAT, format);
902c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                }
9036c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                // Setting a flag in order not to use bulk insert for the file related with
9046c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                // notifications, ringtones, and alarms, because the rowId of the inserted file is
9056c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                // needed.
9066c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                if (mWasEmptyPriorToScan) {
9076c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                    if (notifications && !mDefaultNotificationSet) {
9086c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                        if (TextUtils.isEmpty(mDefaultNotificationFilename) ||
9096c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                                doesPathHaveFilename(entry.mPath, mDefaultNotificationFilename)) {
9106c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                            needToSetSettings = true;
9116c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                        }
9126c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                    } else if (ringtones && !mDefaultRingtoneSet) {
9136c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                        if (TextUtils.isEmpty(mDefaultRingtoneFilename) ||
9146c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                                doesPathHaveFilename(entry.mPath, mDefaultRingtoneFilename)) {
9156c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                            needToSetSettings = true;
9166c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                        }
9176c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                    } else if (alarms && !mDefaultAlarmSet) {
9186c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                        if (TextUtils.isEmpty(mDefaultAlarmAlertFilename) ||
9196c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                                doesPathHaveFilename(entry.mPath, mDefaultAlarmAlertFilename)) {
9206c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                            needToSetSettings = true;
9216c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                        }
9226c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                    }
9236c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                }
9246c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim
925d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // New file, insert it.
926d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // Directories need to be inserted before the files they contain, so they
927d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // get priority when bulk inserting.
928d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // If the rowId of the inserted file is needed, it gets inserted immediately,
929d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                // bypassing the bulk inserter.
930d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                if (inserter == null || needToSetSettings) {
93145aaa2a345292bb6d600822db602f25e261c0e50Mike Lockwood                    result = mMediaProvider.insert(tableUri, values);
932d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) {
933d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                    inserter.insertwithPriority(tableUri, values);
934c6eaa713a3f5b9337eca60ba2ab570cf6e80c357Mike Lockwood                } else {
9358e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                    inserter.insert(tableUri, values);
936bd682b040833fce9e212c00c395b32bec7050f87Mike Lockwood                }
937bd682b040833fce9e212c00c395b32bec7050f87Mike Lockwood
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (result != null) {
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    rowId = ContentUris.parseId(result);
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    entry.mRowId = rowId;
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // updated file
94445aaa2a345292bb6d600822db602f25e261c0e50Mike Lockwood                result = ContentUris.withAppendedId(tableUri, rowId);
9450dd9f4de3ed681e83bd79ddcd4fd463093d11ed3Mike Lockwood                // path should never change, and we want to avoid replacing mixed cased paths
9460dd9f4de3ed681e83bd79ddcd4fd463093d11ed3Mike Lockwood                // with squashed lower case paths
9470dd9f4de3ed681e83bd79ddcd4fd463093d11ed3Mike Lockwood                values.remove(MediaStore.MediaColumns.DATA);
948ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen
949ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                int mediaType = 0;
950ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                if (!MediaScanner.isNoMediaPath(entry.mPath)) {
951ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    int fileType = MediaFile.getFileTypeForMimeType(mMimeType);
952ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    if (MediaFile.isAudioFileType(fileType)) {
953ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        mediaType = FileColumns.MEDIA_TYPE_AUDIO;
954ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    } else if (MediaFile.isVideoFileType(fileType)) {
955ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        mediaType = FileColumns.MEDIA_TYPE_VIDEO;
956ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    } else if (MediaFile.isImageFileType(fileType)) {
957ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        mediaType = FileColumns.MEDIA_TYPE_IMAGE;
958ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    } else if (MediaFile.isPlayListFileType(fileType)) {
959ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                        mediaType = FileColumns.MEDIA_TYPE_PLAYLIST;
960ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    }
961ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                    values.put(FileColumns.MEDIA_TYPE, mediaType);
962ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen                }
963ca78f3d9aff78c35bc065b0c35a0b3c917f56435Marco Nelissen
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mMediaProvider.update(result, values, null, null);
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
96620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9676c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim            if(needToSetSettings) {
9686c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                if (notifications) {
96945aaa2a345292bb6d600822db602f25e261c0e50Mike Lockwood                    setSettingIfNotSet(Settings.System.NOTIFICATION_SOUND, tableUri, rowId);
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mDefaultNotificationSet = true;
9716c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                } else if (ringtones) {
97245aaa2a345292bb6d600822db602f25e261c0e50Mike Lockwood                    setSettingIfNotSet(Settings.System.RINGTONE, tableUri, rowId);
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mDefaultRingtoneSet = true;
9746c4c15569674e6a5eda0565b501b600a50d4ca6eSungsoo Lim                } else if (alarms) {
97545aaa2a345292bb6d600822db602f25e261c0e50Mike Lockwood                    setSettingIfNotSet(Settings.System.ALARM_ALERT, tableUri, rowId);
9763156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                    mDefaultAlarmSet = true;
9773156bb00dd3ab3d6ec732bb6a64a2e84d08c34f0Patrick Scott                }
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
97920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return result;
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
98220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private boolean doesPathHaveFilename(String path, String filename) {
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int pathFilenameStart = path.lastIndexOf(File.separatorChar) + 1;
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int filenameLength = filename.length();
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return path.regionMatches(pathFilenameStart, filename, 0, filenameLength) &&
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    pathFilenameStart + filenameLength == path.length();
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
98920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        private void setSettingIfNotSet(String settingName, Uri uri, long rowId) {
99120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String existingSettingValue = Settings.System.getString(mContext.getContentResolver(),
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    settingName);
99420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (TextUtils.isEmpty(existingSettingValue)) {
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Set the setting to the given URI
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Settings.System.putString(mContext.getContentResolver(), settingName,
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ContentUris.withAppendedId(uri, rowId).toString());
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
100120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10025d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang        private int getFileTypeFromDrm(String path) {
1003e4eb4d9de37a8e9ed6f43a5f7deccf88f28bfc8bMarco Nelissen            if (!isDrmEnabled()) {
10045d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                return 0;
10055d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
10065d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
10075d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            int resultFileType = 0;
10085d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
10095d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            if (mDrmManagerClient == null) {
10105d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                mDrmManagerClient = new DrmManagerClient(mContext);
10115d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
10125d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
10135d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            if (mDrmManagerClient.canHandle(path, null)) {
10145d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                String drmMimetype = mDrmManagerClient.getOriginalMimeType(path);
10155d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                if (drmMimetype != null) {
10165d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                    mMimeType = drmMimetype;
10175d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                    resultFileType = MediaFile.getFileTypeForMimeType(drmMimetype);
10185d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang                }
10195d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            }
10205d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang            return resultFileType;
10215d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang        }
10225d143ad4a8f8c90f44e185a494ff3ff638e6c103Gloria Wang
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }; // end of anonymous MediaScannerClient instance
102420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1025c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    private void prescan(String filePath, boolean prescanFiles) throws RemoteException {
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Cursor c = null;
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String where = null;
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String[] selectionArgs = null;
102920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mPlayLists == null) {
103158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            mPlayLists = new ArrayList<FileEntry>();
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPlayLists.clear();
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
103520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1036c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        if (filePath != null) {
1037c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            // query for only one file
103858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            where = MediaStore.Files.FileColumns._ID + ">?" +
103958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                " AND " + Files.FileColumns.DATA + "=?";
104058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            selectionArgs = new String[] { "", filePath };
104158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        } else {
104258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            where = MediaStore.Files.FileColumns._ID + ">?";
104358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            selectionArgs = new String[] { "" };
1044c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
1045c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
104658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        // Tell the provider to not delete the file.
104758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        // If the file is truly gone the delete is unnecessary, and we want to avoid
104858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        // accidentally deleting files that are really there (this may happen if the
104958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        // filesystem is mounted and unmounted while the scanner is running).
105058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        Uri.Builder builder = mFilesUri.buildUpon();
105158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
105258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
105358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Build the list of files from the content provider
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
1056c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            if (prescanFiles) {
105758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                // First read existing files from the files table.
105858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                // Because we'll be deleting entries for missing files as we go,
105958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                // we need to query the database in small batches, to avoid problems
106058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                // with CursorWindow positioning.
106158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                long lastId = Long.MIN_VALUE;
106258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
106358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                mWasEmptyPriorToScan = true;
106458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
106558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                while (true) {
106658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    selectionArgs[0] = "" + lastId;
106758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    if (c != null) {
106858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        c.close();
106958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        c = null;
107058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    }
107158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
107258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
107358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    if (c == null) {
107458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        break;
107558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    }
107620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
107758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    int num = c.getCount();
1078c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
107958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    if (num == 0) {
108058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        break;
108158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    }
108258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    mWasEmptyPriorToScan = false;
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    while (c.moveToNext()) {
1084d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
1085d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
1086d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
1087d7456c650ef4e105f9835236b79244d906e2f1e0Mike Lockwood                        long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
108858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                        lastId = rowId;
108920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
109035b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // Only consider entries with absolute path names.
109135b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // This allows storing URIs in the database without the
109235b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                        // media scanner removing them.
10933e5e8055942ee8179675b8ed11d40ca12dbe42e6Marco Nelissen                        if (path != null && path.startsWith("/")) {
109458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            boolean exists = false;
109558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            try {
109658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK);
109758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            } catch (ErrnoException e1) {
109858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            }
109958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                            if (!exists && !MtpConstants.isAbstractObject(format)) {
110058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                // do not delete missing playlists, since they may have been
110158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                // modified by the user.
110258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                // The user can delete them in the media player instead.
110358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                // instead, clear the path and lastModified fields in the row
110458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
110558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
110658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
110758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                if (!MediaFile.isPlayListFileType(fileType)) {
110858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                    deleter.delete(rowId);
110958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                    if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
111058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                        deleter.flush();
111158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                        String parent = new File(path).getParent();
111258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                        mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
111358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                    }
111458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                                }
111535b002a9c873fb40116179c8626b9841b0abae74Marco Nelissen                            }
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        finally {
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != null) {
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.close();
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
112558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            deleter.flush();
11269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11276458935331ccfacb4cccc50d6b893947febae687Mike Lockwood
11286458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        // compute original size of images
11296458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        mOriginalCount = 0;
113075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown        c = mMediaProvider.query(mImagesUri, ID_PROJECTION, null, null, null, null);
11316458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        if (c != null) {
11326458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            mOriginalCount = c.getCount();
11336458935331ccfacb4cccc50d6b893947febae687Mike Lockwood            c.close();
11346458935331ccfacb4cccc50d6b893947febae687Mike Lockwood        }
11359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
113620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean inScanDirectory(String path, String[] directories) {
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < directories.length; i++) {
1139f45853af5051df4abaa33cdca7fa986104811d1aMike Lockwood            String directory = directories[i];
1140f45853af5051df4abaa33cdca7fa986104811d1aMike Lockwood            if (path.startsWith(directory)) {
11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return true;
11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
114620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void pruneDeadThumbnailFiles() {
11489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        HashSet<String> existingFiles = new HashSet<String>();
11499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String directory = "/sdcard/DCIM/.thumbnails";
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String [] files = (new File(directory)).list();
11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (files == null)
11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            files = new String[0];
115320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < files.length; i++) {
11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String fullPathString = directory + "/" + files[i];
11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            existingFiles.add(fullPathString);
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
115820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Cursor c = mMediaProvider.query(
116120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    mThumbsUri,
116220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    new String [] { "_data" },
116320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    null,
116420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync                    null,
116575ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                    null, null);
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.v(TAG, "pruneDeadThumbnailFiles... " + c);
11679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != null && c.moveToFirst()) {
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                do {
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String fullPathString = c.getString(0);
11709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    existingFiles.remove(fullPathString);
11719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } while (c.moveToNext());
11729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
117320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (String fileToDelete : existingFiles) {
117543a17654cf4bfe7f1ec22bd8b7b32daccdf27c09Joe Onorato                if (false)
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Log.v(TAG, "fileToDelete is " + fileToDelete);
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    (new File(fileToDelete)).delete();
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (SecurityException ex) {
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
118220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.v(TAG, "/pruneDeadThumbnailFiles... " + c);
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (c != null) {
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                c.close();
11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // We will soon be killed...
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11929ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen    static class MediaBulkDeleter {
1193d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen        StringBuilder whereClause = new StringBuilder();
1194872e7b7073fad8295e6314c73b87d7cb3f85d39cRay Chen        ArrayList<String> whereArgs = new ArrayList<String>(100);
11959ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        IContentProvider mProvider;
11969ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        Uri mBaseUri;
11979ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen
11989ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        public MediaBulkDeleter(IContentProvider provider, Uri baseUri) {
11999ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen            mProvider = provider;
12009ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen            mBaseUri = baseUri;
12019ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        }
12029ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen
12039ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        public void delete(long id) throws RemoteException {
1204d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            if (whereClause.length() != 0) {
1205d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                whereClause.append(",");
12069ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen            }
1207d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            whereClause.append("?");
1208d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            whereArgs.add("" + id);
1209d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            if (whereArgs.size() > 100) {
12109ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen                flush();
12119ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen            }
12129ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        }
12139ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        public void flush() throws RemoteException {
1214d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            int size = whereArgs.size();
1215d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            if (size > 0) {
1216d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                String [] foo = new String [size];
1217d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                foo = whereArgs.toArray(foo);
1218d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                int numrows = mProvider.delete(mBaseUri, MediaStore.MediaColumns._ID + " IN (" +
1219d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                        whereClause.toString() + ")", foo);
1220d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                //Log.i("@@@@@@@@@", "rows deleted: " + numrows);
1221d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                whereClause.setLength(0);
1222d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen                whereArgs.clear();
1223d121cfcbb45cb9ca734e702861f0bdd422999cf5Marco Nelissen            }
12249ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen        }
12259ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen    }
12269ff4774cac499a376a59373d5bfb5112c9a2a004Marco Nelissen
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void postscan(String[] directories) throws RemoteException {
122820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // handle playlists last, after we know what media files are on the storage.
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mProcessPlaylists) {
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            processPlayLists();
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
123320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mOriginalCount == 0 && mImagesUri.equals(Images.Media.getContentUri("external")))
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            pruneDeadThumbnailFiles();
123620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // allow GC to clean up
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mPlayLists = null;
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMediaProvider = null;
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
124120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void initialize(String volumeName) {
12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mMediaProvider = mContext.getContentResolver().acquireProvider("media");
124420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mAudioUri = Audio.Media.getContentUri(volumeName);
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mVideoUri = Video.Media.getContentUri(volumeName);
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mImagesUri = Images.Media.getContentUri(volumeName);
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mThumbsUri = Images.Thumbnails.getContentUri(volumeName);
1249c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        mFilesUri = Files.getContentUri(volumeName);
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!volumeName.equals("internal")) {
12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // we only support playlists on external media
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mProcessPlaylists = true;
125443b076bb75e86fbb34cbe36aa0298c7a647121ffMike Lockwood            mProcessGenres = true;
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mPlaylistsUri = Playlists.getContentUri(volumeName);
1256f73738b78a8396552274cf33b0021f414fb7201dMike Lockwood
1257f50e54e64b2b5a269e1a88c511a57064a14bb51bDianne Hackborn            mCaseInsensitivePaths = true;
125820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        }
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public void scanDirectories(String[] directories, String volumeName) {
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long start = System.currentTimeMillis();
126420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            initialize(volumeName);
1265c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            prescan(null, true);
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long prescan = System.currentTimeMillis();
1267e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood
12683410b4656505673c72da558dcfec94d7b135e760Mike Lockwood            if (ENABLE_BULK_INSERTS) {
12698e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                // create MediaInserter for bulk inserts
12708e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                mMediaInserter = new MediaInserter(mMediaProvider, 500);
12713410b4656505673c72da558dcfec94d7b135e760Mike Lockwood            }
127220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < directories.length; i++) {
1274c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood                processDirectory(directories[i], mClient);
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1276e19572ccb5e7963eee9dd1cca150ee752a850e4aMike Lockwood
12773410b4656505673c72da558dcfec94d7b135e760Mike Lockwood            if (ENABLE_BULK_INSERTS) {
12783410b4656505673c72da558dcfec94d7b135e760Mike Lockwood                // flush remaining inserts
12798e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                mMediaInserter.flushAll();
12808e2ed8d512ade2ea481b3a292556782b17a74674Dongwon Kang                mMediaInserter = null;
12813410b4656505673c72da558dcfec94d7b135e760Mike Lockwood            }
1282bd682b040833fce9e212c00c395b32bec7050f87Mike Lockwood
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long scan = System.currentTimeMillis();
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            postscan(directories);
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            long end = System.currentTimeMillis();
128620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
128743a17654cf4bfe7f1ec22bd8b7b32daccdf27c09Joe Onorato            if (false) {
12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, " prescan time: " + (prescan - start) + "ms\n");
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "    scan time: " + (scan - prescan) + "ms\n");
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "postscan time: " + (end - scan) + "ms\n");
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.d(TAG, "   total time: " + (end - start) + "ms\n");
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (SQLException e) {
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // this might happen if the SD card is removed while the media scanner is running
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "SQLException in MediaScanner.scan()", e);
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (UnsupportedOperationException e) {
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // this might happen if the SD card is removed while the media scanner is running
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "UnsupportedOperationException in MediaScanner.scan()", e);
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "RemoteException in MediaScanner.scan()", e);
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // this function is used to scan a single file
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Uri scanSingleFile(String path, String volumeName, String mimeType) {
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
130720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync            initialize(volumeName);
1308c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            prescan(path, true);
130920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File file = new File(path);
1311ec74abb71f79cdc573c536115acc7ff26a02680aMikael
1312ec74abb71f79cdc573c536115acc7ff26a02680aMikael            // lastModified is in milliseconds on Files.
1313ec74abb71f79cdc573c536115acc7ff26a02680aMikael            long lastModifiedSeconds = file.lastModified() / 1000;
1314ec74abb71f79cdc573c536115acc7ff26a02680aMikael
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // always scan the file, so we can return the content://media Uri for existing files
13167a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            return mClient.doScanFile(path, mimeType, lastModifiedSeconds, file.length(),
1317997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    false, true, false);
13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1324ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood    private static boolean isNoMediaFile(String path) {
1325ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        File file = new File(path);
1326ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        if (file.isDirectory()) return false;
1327ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood
1328ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        // special case certain file names
1329ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        // I use regionMatches() instead of substring() below
1330ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        // to avoid memory allocation
1331ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        int lastSlash = path.lastIndexOf('/');
1332ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        if (lastSlash >= 0 && lastSlash + 2 < path.length()) {
1333ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            // ignore those ._* files created by MacOS
1334ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            if (path.regionMatches(lastSlash + 1, "._", 0, 2)) {
1335ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                return true;
1336ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            }
1337ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood
1338ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            // ignore album art files created by Windows Media Player:
1339ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            // Folder.jpg, AlbumArtSmall.jpg, AlbumArt_{...}_Large.jpg
1340ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            // and AlbumArt_{...}_Small.jpg
1341ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            if (path.regionMatches(true, path.length() - 4, ".jpg", 0, 4)) {
1342ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                if (path.regionMatches(true, lastSlash + 1, "AlbumArt_{", 0, 10) ||
1343ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                        path.regionMatches(true, lastSlash + 1, "AlbumArt.", 0, 9)) {
1344ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                    return true;
1345ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                }
1346ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                int length = path.length() - lastSlash - 1;
1347ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                if ((length == 17 && path.regionMatches(
1348ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                        true, lastSlash + 1, "AlbumArtSmall", 0, 13)) ||
1349ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                        (length == 10
1350ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                         && path.regionMatches(true, lastSlash + 1, "Folder", 0, 6))) {
1351ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                    return true;
1352ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood                }
1353ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood            }
1354ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        }
1355ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        return false;
1356ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood    }
1357ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood
1358997354e4b4a9666cedd62282471e97822affced9Mike Lockwood    public static boolean isNoMediaPath(String path) {
1359997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        if (path == null) return false;
1360997354e4b4a9666cedd62282471e97822affced9Mike Lockwood
1361997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        // return true if file or any parent directory has name starting with a dot
1362997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        if (path.indexOf("/.") >= 0) return true;
1363997354e4b4a9666cedd62282471e97822affced9Mike Lockwood
1364997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        // now check to see if any parent directories have a ".nomedia" file
1365997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        // start from 1 so we don't bother checking in the root directory
1366997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        int offset = 1;
1367997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        while (offset >= 0) {
1368997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            int slashIndex = path.indexOf('/', offset);
1369997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            if (slashIndex > offset) {
1370997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                slashIndex++; // move past slash
1371997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                File file = new File(path.substring(0, slashIndex) + ".nomedia");
1372997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                if (file.exists()) {
1373997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    // we have a .nomedia in one of the parent directories
1374997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    return true;
1375997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                }
1376997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            }
1377997354e4b4a9666cedd62282471e97822affced9Mike Lockwood            offset = slashIndex;
1378997354e4b4a9666cedd62282471e97822affced9Mike Lockwood        }
1379ade06df0fe3499d66ee5cc29071d41445d1091faMike Lockwood        return isNoMediaFile(path);
1380997354e4b4a9666cedd62282471e97822affced9Mike Lockwood    }
1381997354e4b4a9666cedd62282471e97822affced9Mike Lockwood
1382c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    public void scanMtpFile(String path, String volumeName, int objectHandle, int format) {
13837a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood        initialize(volumeName);
1384c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
1385c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
13867a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood        File file = new File(path);
13877a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood        long lastModifiedSeconds = file.lastModified() / 1000;
1388c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1389c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
1390c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            !MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType)) {
13917a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood
13927a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            // no need to use the media scanner, but we need to update last modified and file size
13937a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            ContentValues values = new ContentValues();
13947a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            values.put(Files.FileColumns.SIZE, file.length());
13957a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
13967a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            try {
13977a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood                String[] whereArgs = new String[] {  Integer.toString(objectHandle) };
13987a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood                mMediaProvider.update(Files.getMtpObjectsUri(volumeName), values, "_id=?",
13997a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood                        whereArgs);
14007a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            } catch (RemoteException e) {
14017a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood                Log.e(TAG, "RemoteException in scanMtpFile", e);
14027a0bd17bceaf3efc3732e30c538fae420d3b742cMike Lockwood            }
1403c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            return;
1404c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
1405c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1406d815f79766984fce499e147ecbacc01914683f74Mike Lockwood        mMtpObjectHandle = objectHandle;
1407c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        Cursor fileList = null;
1408c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        try {
1409caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            if (MediaFile.isPlayListFileType(fileType)) {
1410caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                // build file cache so we can look up tracks in the playlist
1411caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                prescan(null, true);
1412caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood
141358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                FileEntry entry = makeEntryFor(path);
1414caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                if (entry != null) {
1415c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                    fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
1416c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                            null, null, null, null);
1417c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                    processPlayList(entry, fileList);
1418caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                }
1419caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            } else {
1420caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                // MTP will create a file entry for us so we don't want to do it in prescan
1421caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                prescan(path, false);
1422c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood
1423caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood                // always scan the file, so we can return the content://media Uri for existing files
1424076e05b488e40fdd946f0d35137fe66a576efe09Mike Lockwood                mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(),
1425997354e4b4a9666cedd62282471e97822affced9Mike Lockwood                    (format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path));
1426caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            }
1427c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        } catch (RemoteException e) {
1428c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
1429c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        } finally {
1430c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood            mMtpObjectHandle = 0;
1431c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            if (fileList != null) {
1432c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                fileList.close();
1433c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            }
1434c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood        }
1435d815f79766984fce499e147ecbacc01914683f74Mike Lockwood    }
1436d815f79766984fce499e147ecbacc01914683f74Mike Lockwood
143758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen    FileEntry makeEntryFor(String path) {
143858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        String key = path;
143958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        String where;
144058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        String[] selectionArgs;
144158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        if (mCaseInsensitivePaths) {
144258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            where = Files.FileColumns.DATA + " LIKE ?";
144358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            selectionArgs = new String[] { path };
144458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        } else {
144558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            where = Files.FileColumns.DATA + "=?";
144658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            selectionArgs = new String[] { path };
144758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        }
144858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
144958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        Cursor c = null;
145058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        try {
145158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
145258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    where, selectionArgs, null, null);
145358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            if (c.moveToNext()) {
145458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
145558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
145658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
145758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                return new FileEntry(rowId, path, lastModified, format);
145858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            }
145958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        } catch (RemoteException e) {
146058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        } finally {
146158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            if (c != null) {
146258ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                c.close();
146358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen            }
146458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        }
146558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        return null;
146658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen    }
146758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // returns the number of matching file/directory names, starting from the right
14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private int matchPaths(String path1, String path2) {
14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int result = 0;
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end1 = path1.length();
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int end2 = path2.length();
147320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (end1 > 0 && end2 > 0) {
14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int slash1 = path1.lastIndexOf('/', end1 - 1);
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int slash2 = path2.lastIndexOf('/', end2 - 1);
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int backSlash1 = path1.lastIndexOf('\\', end1 - 1);
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int backSlash2 = path2.lastIndexOf('\\', end2 - 1);
14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start1 = (slash1 > backSlash1 ? slash1 : backSlash1);
14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int start2 = (slash2 > backSlash2 ? slash2 : backSlash2);
14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start1 < 0) start1 = 0; else start1++;
14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (start2 < 0) start2 = 0; else start2++;
14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int length = end1 - start1;
14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (end2 - start2 != length) break;
14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path1.regionMatches(true, start1, path2, start2, length)) {
14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                result++;
14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end1 = start1 - 1;
14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                end2 = start2 - 1;
14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else break;
14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
149120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return result;
14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
149520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    private boolean addPlayListEntry(String entry, String playListDirectory,
1496c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            Uri uri, ContentValues values, int index, Cursor fileList) {
149720b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
14989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // watch for trailing whitespace
14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int entryLength = entry.length();
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        while (entryLength > 0 && Character.isWhitespace(entry.charAt(entryLength - 1))) entryLength--;
15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // path should be longer than 3 characters.
15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // avoid index out of bounds errors below by returning here.
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (entryLength < 3) return false;
15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (entryLength < entry.length()) entry = entry.substring(0, entryLength);
15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // does entry appear to be an absolute path?
15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // look for Unix or DOS absolute paths
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        char ch1 = entry.charAt(0);
15099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean fullPath = (ch1 == '/' ||
15109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                (Character.isLetter(ch1) && entry.charAt(1) == ':' && entry.charAt(2) == '\\'));
15119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // if we have a relative path, combine entry with playListDirectory
15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!fullPath)
15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            entry = playListDirectory + entry;
151420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //FIXME - should we look for "../" within the path?
151620b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
15179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // best matching MediaFile for the play list entry
151858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        FileEntry bestMatch = null;
151920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
15209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // number of rightmost file/directory names for bestMatch
152120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        int bestMatchLength = 0;
152220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1523c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        if (fileList != null) {
1524c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            int count = fileList.getCount();
1525c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            // Backing up a little in the cursor helps when the files in the
1526c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            // playlist are not in the same order as they are in the database
1527c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            // but are still close.
1528c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            fileList.move(-1000);
1529c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            while(--count >= 0) {
1530c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                if (!fileList.moveToNext()) {
1531c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                    fileList.moveToFirst();
1532c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                }
1533c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                long rowId = fileList.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
1534c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                String path = fileList.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
1535c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                int format = fileList.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
1536c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                long lastModified = fileList.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
153758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen
153858ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                if (path.equalsIgnoreCase(entry)) {
153958ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    bestMatch = new FileEntry(rowId, path, lastModified, format);
154058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    break;    // don't bother continuing search
154158ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                }
154220b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
154358ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                int matchLength = matchPaths(path, entry);
154458ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                if (matchLength > bestMatchLength) {
154558ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    bestMatch = new FileEntry(rowId, path, lastModified, format);
154658ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                    bestMatchLength = matchLength;
154758ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen                }
15489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
155020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1551caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        if (bestMatch == null) {
15529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
15539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
155420b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
15559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
15567d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang            // check rowid is set. Rowid may be missing if it is inserted by bulkInsert().
15577d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang            if (bestMatch.mRowId == 0) {
1558c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                Cursor c = mMediaProvider.query(mAudioUri, ID_PROJECTION,
15597d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                        MediaStore.Files.FileColumns.DATA + "=?",
156075ea64fc54f328d37b115cfb1ded1e45c30380edJeff Brown                        new String[] { bestMatch.mPath }, null, null);
15617d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                if (c != null) {
15627d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                    if (c.moveToNext()) {
15637d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                        bestMatch.mRowId = c.getLong(0);
15647d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                    }
15657d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                    c.close();
15667d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                }
15677d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                if (bestMatch.mRowId == 0) {
15687d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                    return false;
15697d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang                }
15707d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang            }
15717d92a22d9dec42a45cd55fd77d0a86b4b9c6e9dfDongwon Kang            // OK, now we are ready to add this to the database
15729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            values.clear();
15739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Integer.valueOf(index));
15749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.valueOf(bestMatch.mRowId));
15759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mMediaProvider.insert(uri, values);
15769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (RemoteException e) {
15779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "RemoteException in MediaScanner.addPlayListEntry()", e);
15789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
15799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
15829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
158320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1584c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen    private void processM3uPlayList(String path, String playListDirectory, Uri uri,
1585c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            ContentValues values, Cursor fileList) {
15869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedReader reader = null;
15879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
15889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
15899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
15909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                reader = new BufferedReader(
15919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        new InputStreamReader(new FileInputStream(f)), 8192);
15929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String line = reader.readLine();
15939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int index = 0;
15949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (line != null) {
15959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // ignore comment lines, which begin with '#'
15969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (line.length() > 0 && line.charAt(0) != '#') {
15979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        values.clear();
1598c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                        if (addPlayListEntry(line, playListDirectory, uri, values, index, fileList))
15999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            index++;
16009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
16019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    line = reader.readLine();
16029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
16039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
16059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
16069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
16079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
16089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (reader != null)
16099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    reader.close();
16109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
16119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processM3uPlayList()", e);
16129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1616c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen    private void processPlsPlayList(String path, String playListDirectory, Uri uri,
1617c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            ContentValues values, Cursor fileList) {
16189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedReader reader = null;
16199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
16209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
16219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
16229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                reader = new BufferedReader(
16239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        new InputStreamReader(new FileInputStream(f)), 8192);
16249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                String line = reader.readLine();
16259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int index = 0;
16269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (line != null) {
16279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // ignore comment lines, which begin with '#'
16289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (line.startsWith("File")) {
16299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        int equals = line.indexOf('=');
16309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (equals > 0) {
16319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            values.clear();
1632c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                            if (addPlayListEntry(line.substring(equals + 1), playListDirectory,
1633c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                                    uri, values, index, fileList))
16349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                index++;
16359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
16369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
16379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    line = reader.readLine();
16389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
16399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
16419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
16429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
16439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
16449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (reader != null)
16459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    reader.close();
16469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
16479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processPlsPlayList()", e);
16489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    class WplHandler implements ElementListener {
16539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ContentHandler handler;
16559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String playListDirectory;
16569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Uri uri;
1657c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        Cursor fileList;
16589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ContentValues values = new ContentValues();
16599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int index = 0;
16609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1661c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        public WplHandler(String playListDirectory, Uri uri, Cursor fileList) {
16629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.playListDirectory = playListDirectory;
16639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.uri = uri;
1664c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            this.fileList = fileList;
166520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
16669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            RootElement root = new RootElement("smil");
16679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element body = root.getChild("body");
16689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element seq = body.getChild("seq");
16699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Element media = seq.getChild("media");
16709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            media.setElementListener(this);
16719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.handler = root.getContentHandler();
16739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1675c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        @Override
16769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void start(Attributes attributes) {
16779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String path = attributes.getValue("", "src");
16789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (path != null) {
16799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                values.clear();
1680c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                if (addPlayListEntry(path, playListDirectory, uri, values, index, fileList)) {
16819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    index++;
16829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
16839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       public void end() {
16879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project       }
16889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ContentHandler getContentHandler() {
16909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return handler;
16919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1694c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen    private void processWplPlayList(String path, String playListDirectory, Uri uri,
1695c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            Cursor fileList) {
16969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        FileInputStream fis = null;
16979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
16989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File f = new File(path);
16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (f.exists()) {
17009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                fis = new FileInputStream(f);
17019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1702c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                Xml.parse(fis, Xml.findEncodingByName("UTF-8"),
1703c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                        new WplHandler(playListDirectory, uri, fileList).getContentHandler());
17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (SAXException e) {
17069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.printStackTrace();
17079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
17089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            e.printStackTrace();
17099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
17109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
17119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fis != null)
17129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fis.close();
17139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
17149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e(TAG, "IOException in MediaScanner.processWplPlayList()", e);
17159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
171820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1719c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen    private void processPlayList(FileEntry entry, Cursor fileList) throws RemoteException {
1720caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        String path = entry.mPath;
1721caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        ContentValues values = new ContentValues();
1722caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        int lastSlash = path.lastIndexOf('/');
1723caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        if (lastSlash < 0) throw new IllegalArgumentException("bad path " + path);
1724caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        Uri uri, membersUri;
1725caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        long rowId = entry.mRowId;
1726caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood
1727242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        // make sure we have a name
1728242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        String name = values.getAsString(MediaStore.Audio.Playlists.NAME);
1729242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        if (name == null) {
1730242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood            name = values.getAsString(MediaStore.MediaColumns.TITLE);
1731242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood            if (name == null) {
1732242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood                // extract name from file name
1733242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood                int lastDot = path.lastIndexOf('.');
1734242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood                name = (lastDot < 0 ? path.substring(lastSlash + 1)
1735242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood                        : path.substring(lastSlash + 1, lastDot));
1736242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood            }
1737242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        }
1738242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood
1739242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        values.put(MediaStore.Audio.Playlists.NAME, name);
1740242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        values.put(MediaStore.Audio.Playlists.DATE_MODIFIED, entry.mLastModified);
1741242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood
1742242d0cdc1ad6c48faca403ea352bde73138443b5Mike Lockwood        if (rowId == 0) {
1743caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            values.put(MediaStore.Audio.Playlists.DATA, path);
1744caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            uri = mMediaProvider.insert(mPlaylistsUri, values);
1745caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            rowId = ContentUris.parseId(uri);
1746caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
1747caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        } else {
1748caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            uri = ContentUris.withAppendedId(mPlaylistsUri, rowId);
1749caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            mMediaProvider.update(uri, values, null, null);
175020b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1751caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            // delete members of existing playlist
1752caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            membersUri = Uri.withAppendedPath(uri, Playlists.Members.CONTENT_DIRECTORY);
1753caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood            mMediaProvider.delete(membersUri, null, null);
1754caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        }
17559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1756caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        String playListDirectory = path.substring(0, lastSlash + 1);
1757caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
1758caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
175920b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1760caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        if (fileType == MediaFile.FILE_TYPE_M3U) {
1761c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            processM3uPlayList(path, playListDirectory, membersUri, values, fileList);
1762caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        } else if (fileType == MediaFile.FILE_TYPE_PLS) {
1763c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            processPlsPlayList(path, playListDirectory, membersUri, values, fileList);
1764caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        } else if (fileType == MediaFile.FILE_TYPE_WPL) {
1765c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            processWplPlayList(path, playListDirectory, membersUri, fileList);
1766caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood        }
1767caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood    }
176820b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1769caf7e43a40245520fc26b19bce408da24c050e01Mike Lockwood    private void processPlayLists() throws RemoteException {
177058ef68905d67e356eb6a05176863cacfbdca75c6Marco Nelissen        Iterator<FileEntry> iterator = mPlayLists.iterator();
1771c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        Cursor fileList = null;
1772c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        try {
177308f70fa5b82faa00d4d536b5a220d7ce8dd8f932Marco Nelissen            // use the files uri and projection because we need the format column,
177408f70fa5b82faa00d4d536b5a220d7ce8dd8f932Marco Nelissen            // but restrict the query to just audio files
1775c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            fileList = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
177608f70fa5b82faa00d4d536b5a220d7ce8dd8f932Marco Nelissen                    "media_type=2", null, null, null);
1777c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            while (iterator.hasNext()) {
1778c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                FileEntry entry = iterator.next();
1779c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                // only process playlist files if they are new or have been modified since the last scan
1780c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                if (entry.mLastModifiedChanged) {
1781c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                    processPlayList(entry, fileList);
1782c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                }
1783c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            }
1784c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        } catch (RemoteException e1) {
1785c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen        } finally {
1786c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen            if (fileList != null) {
1787c164faa8314c110d417b653b9926de4803bb2ddaMarco Nelissen                fileList.close();
17889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
179120b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
1792c37255d5d0fd9e0ec02b0d7cb5c4b235e200d367Mike Lockwood    private native void processDirectory(String path, MediaScannerClient client);
17939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native void processFile(String path, String mimeType, MediaScannerClient client);
17949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public native void setLocale(String locale);
179520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync
17969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public native byte[] extractAlbumArt(FileDescriptor fd);
17979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17984935d05eaa306cef88cf0ab13eca386f270409ecMarco Nelissen    private static native final void native_init();
17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native final void native_setup();
18009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private native final void native_finalize();
1801133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong
1802133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong    /**
1803133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong     * Releases resouces associated with this MediaScanner object.
1804133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong     * It is considered good practice to call this method when
1805133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong     * one is done using the MediaScanner object. After this method
1806133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong     * is called, the MediaScanner object can no longer be used.
1807133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong     */
1808133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong    public void release() {
1809133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong        native_finalize();
1810133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong    }
1811133cf8b92a07d5be8c64607b9644aa9cef169fb0James Dong
18129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
181320b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync    protected void finalize() {
18149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mContext.getContentResolver().releaseProvider(mMediaProvider);
181520b03ea70bda3c4fb34e53cdf25cf98c4adb193frepo sync        native_finalize();
18169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1818