MediaScannerConnection.java revision b7c8c76180dc1abbf55c734ab121a7a2469060f6
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.media;
18
19import android.content.ComponentName;
20import android.content.Context;
21import android.content.Intent;
22import android.content.ServiceConnection;
23import android.media.IMediaScannerListener;
24import android.media.IMediaScannerService;
25import android.net.Uri;
26import android.os.IBinder;
27import android.os.RemoteException;
28import android.util.Config;
29import android.util.Log;
30
31
32/**
33 * MediaScannerConnection provides a way for applications to pass a
34 * newly created or downloaded media file to the media scanner service.
35 * The media scanner service will read metadata from the file and add
36 * the file to the media content provider.
37 * The MediaScannerConnectionClient provides an interface for the
38 * media scanner service to return the Uri for a newly scanned file
39 * to the client of the MediaScannerConnection class.
40 */
41public class MediaScannerConnection implements ServiceConnection {
42
43    private static final String TAG = "MediaScannerConnection";
44
45    private Context mContext;
46    private MediaScannerConnectionClient mClient;
47    private IMediaScannerService mService;
48    private boolean mConnected; // true if connect() has been called since last disconnect()
49
50    private IMediaScannerListener.Stub mListener = new IMediaScannerListener.Stub() {
51        public void scanCompleted(String path, Uri uri) {
52            MediaScannerConnectionClient client = mClient;
53            if (client != null) {
54                client.onScanCompleted(path, uri);
55            }
56        }
57    };
58
59    /**
60     * Interface for notifying clients of the result of scanning a
61     * requested media file.
62     */
63    public interface OnScanCompletedListener {
64        /**
65         * Called to notify the client when the media scanner has finished
66         * scanning a file.
67         * @param path the path to the file that has been scanned.
68         * @param uri the Uri for the file if the scanning operation succeeded
69         * and the file was added to the media database, or null if scanning failed.
70         */
71        public void onScanCompleted(String path, Uri uri);
72    }
73
74    /**
75     * An interface for notifying clients of MediaScannerConnection
76     * when a connection to the MediaScanner service has been established
77     * and when the scanning of a file has completed.
78     */
79    public interface MediaScannerConnectionClient extends OnScanCompletedListener {
80        /**
81         * Called to notify the client when a connection to the
82         * MediaScanner service has been established.
83         */
84        public void onMediaScannerConnected();
85
86        /**
87         * Called to notify the client when the media scanner has finished
88         * scanning a file.
89         * @param path the path to the file that has been scanned.
90         * @param uri the Uri for the file if the scanning operation succeeded
91         * and the file was added to the media database, or null if scanning failed.
92         */
93        public void onScanCompleted(String path, Uri uri);
94    }
95
96    /**
97     * Constructs a new MediaScannerConnection object.
98     * @param context the Context object, required for establishing a connection to
99     * the media scanner service.
100     * @param client an optional object implementing the MediaScannerConnectionClient
101     * interface, for receiving notifications from the media scanner.
102     */
103    public MediaScannerConnection(Context context, MediaScannerConnectionClient client) {
104        mContext = context;
105        mClient = client;
106    }
107
108    /**
109     * Initiates a connection to the media scanner service.
110     * {@link MediaScannerConnectionClient#onMediaScannerConnected()}
111     * will be called when the connection is established.
112     */
113    public void connect() {
114        synchronized (this) {
115            if (!mConnected) {
116                Intent intent = new Intent(IMediaScannerService.class.getName());
117                mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
118                mConnected = true;
119            }
120        }
121    }
122
123    /**
124     * Releases the connection to the media scanner service.
125     */
126    public void disconnect() {
127        synchronized (this) {
128            if (mConnected) {
129                if (Config.LOGV) {
130                    Log.v(TAG, "Disconnecting from Media Scanner");
131                }
132                try {
133                    mContext.unbindService(this);
134                } catch (IllegalArgumentException ex) {
135                    if (Config.LOGV) {
136                        Log.v(TAG, "disconnect failed: " + ex);
137                    }
138                }
139                mConnected = false;
140            }
141        }
142    }
143
144    /**
145     * Returns whether we are connected to the media scanner service
146     * @return true if we are connected, false otherwise
147     */
148    public synchronized boolean isConnected() {
149        return (mService != null && mConnected);
150    }
151
152    /**
153     * Requests the media scanner to scan a file.
154     * Success or failure of the scanning operation cannot be determined until
155     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
156     *
157     * @param path the path to the file to be scanned.
158     * @param mimeType  an optional mimeType for the file.
159     * If mimeType is null, then the mimeType will be inferred from the file extension.
160     */
161     public void scanFile(String path, String mimeType) {
162        synchronized (this) {
163            if (mService == null || !mConnected) {
164                throw new IllegalStateException("not connected to MediaScannerService");
165            }
166            try {
167                if (Config.LOGV) {
168                    Log.v(TAG, "Scanning file " + path);
169                }
170                mService.requestScanFile(path, mimeType, mListener);
171            } catch (RemoteException e) {
172                if (Config.LOGD) {
173                    Log.d(TAG, "Failed to scan file " + path);
174                }
175            }
176        }
177    }
178
179    static class ClientProxy implements MediaScannerConnectionClient {
180        final String[] mPaths;
181        final String[] mMimeTypes;
182        final OnScanCompletedListener mClient;
183        MediaScannerConnection mConnection;
184        int mNextPath;
185
186        ClientProxy(String[] paths, String[] mimeTypes, OnScanCompletedListener client) {
187            mPaths = paths;
188            mMimeTypes = mimeTypes;
189            mClient = client;
190        }
191
192        public void onMediaScannerConnected() {
193            scanNextPath();
194        }
195
196        public void onScanCompleted(String path, Uri uri) {
197            if (mClient != null) {
198                mClient.onScanCompleted(path, uri);
199            }
200            scanNextPath();
201        }
202
203        void scanNextPath() {
204            if (mNextPath >= mPaths.length) {
205                mConnection.disconnect();
206                return;
207            }
208            String mimeType = mMimeTypes != null ? mMimeTypes[mNextPath] : null;
209            mConnection.scanFile(mPaths[mNextPath], mimeType);
210            mNextPath++;
211        }
212    }
213
214    /**
215     * Convenience for constructing a {@link MediaScannerConnection}, calling
216     * {@link #connect} on it, and calling {@link #scanFile} with the given
217     * <var>path</var> and <var>mimeType</var> when the connection is
218     * established.
219     * @param context The caller's Context, required for establishing a connection to
220     * the media scanner service.
221     * Success or failure of the scanning operation cannot be determined until
222     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
223     * @param paths Array of paths to be scanned.
224     * @param mimeTypes Optional array of MIME types for each path.
225     * If mimeType is null, then the mimeType will be inferred from the file extension.
226     * @param callback Optional callback through which you can receive the
227     * scanned URI and MIME type; If null, the file will be scanned but
228     * you will not get a result back.
229     * @see scanFile(String, String)
230     */
231    public static void scanFile(Context context, String[] paths, String[] mimeTypes,
232            OnScanCompletedListener callback) {
233        ClientProxy client = new ClientProxy(paths, mimeTypes, callback);
234        MediaScannerConnection connection = new MediaScannerConnection(context, client);
235        client.mConnection = connection;
236        connection.connect();
237    }
238
239    /**
240     * Part of the ServiceConnection interface.  Do not call.
241     */
242    public void onServiceConnected(ComponentName className, IBinder service) {
243        if (Config.LOGV) {
244            Log.v(TAG, "Connected to Media Scanner");
245        }
246        synchronized (this) {
247            mService = IMediaScannerService.Stub.asInterface(service);
248            if (mService != null && mClient != null) {
249                mClient.onMediaScannerConnected();
250            }
251        }
252    }
253
254    /**
255     * Part of the ServiceConnection interface.  Do not call.
256     */
257    public void onServiceDisconnected(ComponentName className) {
258        if (Config.LOGV) {
259            Log.v(TAG, "Disconnected from Media Scanner");
260        }
261        synchronized (this) {
262            mService = null;
263        }
264    }
265}
266