/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.media; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.media.IMediaScannerListener; import android.media.IMediaScannerService; import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; /** * MediaScannerConnection provides a way for applications to pass a * newly created or downloaded media file to the media scanner service. * The media scanner service will read metadata from the file and add * the file to the media content provider. * The MediaScannerConnectionClient provides an interface for the * media scanner service to return the Uri for a newly scanned file * to the client of the MediaScannerConnection class. */ public class MediaScannerConnection implements ServiceConnection { private static final String TAG = "MediaScannerConnection"; private Context mContext; private MediaScannerConnectionClient mClient; private IMediaScannerService mService; private boolean mConnected; // true if connect() has been called since last disconnect() private final IMediaScannerListener.Stub mListener = new IMediaScannerListener.Stub() { public void scanCompleted(String path, Uri uri) { MediaScannerConnectionClient client = mClient; if (client != null) { client.onScanCompleted(path, uri); } } }; /** * Interface for notifying clients of the result of scanning a * requested media file. */ public interface OnScanCompletedListener { /** * Called to notify the client when the media scanner has finished * scanning a file. * @param path the path to the file that has been scanned. * @param uri the Uri for the file if the scanning operation succeeded * and the file was added to the media database, or null if scanning failed. */ public void onScanCompleted(String path, Uri uri); } /** * An interface for notifying clients of MediaScannerConnection * when a connection to the MediaScanner service has been established * and when the scanning of a file has completed. */ public interface MediaScannerConnectionClient extends OnScanCompletedListener { /** * Called to notify the client when a connection to the * MediaScanner service has been established. */ public void onMediaScannerConnected(); /** * Called to notify the client when the media scanner has finished * scanning a file. * @param path the path to the file that has been scanned. * @param uri the Uri for the file if the scanning operation succeeded * and the file was added to the media database, or null if scanning failed. */ public void onScanCompleted(String path, Uri uri); } /** * Constructs a new MediaScannerConnection object. * @param context the Context object, required for establishing a connection to * the media scanner service. * @param client an optional object implementing the MediaScannerConnectionClient * interface, for receiving notifications from the media scanner. */ public MediaScannerConnection(Context context, MediaScannerConnectionClient client) { mContext = context; mClient = client; } /** * Initiates a connection to the media scanner service. * {@link MediaScannerConnectionClient#onMediaScannerConnected()} * will be called when the connection is established. */ public void connect() { synchronized (this) { if (!mConnected) { Intent intent = new Intent(IMediaScannerService.class.getName()); intent.setComponent( new ComponentName("com.android.providers.media", "com.android.providers.media.MediaScannerService")); mContext.bindService(intent, this, Context.BIND_AUTO_CREATE); mConnected = true; } } } /** * Releases the connection to the media scanner service. */ public void disconnect() { synchronized (this) { if (mConnected) { if (false) { Log.v(TAG, "Disconnecting from Media Scanner"); } try { mContext.unbindService(this); } catch (IllegalArgumentException ex) { if (false) { Log.v(TAG, "disconnect failed: " + ex); } } mConnected = false; } } } /** * Returns whether we are connected to the media scanner service * @return true if we are connected, false otherwise */ public synchronized boolean isConnected() { return (mService != null && mConnected); } /** * Requests the media scanner to scan a file. * Success or failure of the scanning operation cannot be determined until * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called. * * @param path the path to the file to be scanned. * @param mimeType an optional mimeType for the file. * If mimeType is null, then the mimeType will be inferred from the file extension. */ public void scanFile(String path, String mimeType) { synchronized (this) { if (mService == null || !mConnected) { throw new IllegalStateException("not connected to MediaScannerService"); } try { if (false) { Log.v(TAG, "Scanning file " + path); } mService.requestScanFile(path, mimeType, mListener); } catch (RemoteException e) { if (false) { Log.d(TAG, "Failed to scan file " + path); } } } } static class ClientProxy implements MediaScannerConnectionClient { final String[] mPaths; final String[] mMimeTypes; final OnScanCompletedListener mClient; MediaScannerConnection mConnection; int mNextPath; ClientProxy(String[] paths, String[] mimeTypes, OnScanCompletedListener client) { mPaths = paths; mMimeTypes = mimeTypes; mClient = client; } public void onMediaScannerConnected() { scanNextPath(); } public void onScanCompleted(String path, Uri uri) { if (mClient != null) { mClient.onScanCompleted(path, uri); } scanNextPath(); } void scanNextPath() { if (mNextPath >= mPaths.length) { mConnection.disconnect(); return; } String mimeType = mMimeTypes != null ? mMimeTypes[mNextPath] : null; mConnection.scanFile(mPaths[mNextPath], mimeType); mNextPath++; } } /** * Convenience for constructing a {@link MediaScannerConnection}, calling * {@link #connect} on it, and calling {@link #scanFile} with the given * path and mimeType when the connection is * established. * @param context The caller's Context, required for establishing a connection to * the media scanner service. * Success or failure of the scanning operation cannot be determined until * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called. * @param paths Array of paths to be scanned. * @param mimeTypes Optional array of MIME types for each path. * If mimeType is null, then the mimeType will be inferred from the file extension. * @param callback Optional callback through which you can receive the * scanned URI and MIME type; If null, the file will be scanned but * you will not get a result back. * @see #scanFile(String, String) */ public static void scanFile(Context context, String[] paths, String[] mimeTypes, OnScanCompletedListener callback) { ClientProxy client = new ClientProxy(paths, mimeTypes, callback); MediaScannerConnection connection = new MediaScannerConnection(context, client); client.mConnection = connection; connection.connect(); } /** * Part of the ServiceConnection interface. Do not call. */ public void onServiceConnected(ComponentName className, IBinder service) { if (false) { Log.v(TAG, "Connected to Media Scanner"); } synchronized (this) { mService = IMediaScannerService.Stub.asInterface(service); if (mService != null && mClient != null) { mClient.onMediaScannerConnected(); } } } /** * Part of the ServiceConnection interface. Do not call. */ public void onServiceDisconnected(ComponentName className) { if (false) { Log.v(TAG, "Disconnected from Media Scanner"); } synchronized (this) { mService = null; } } }