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     * An interface for notifying clients of MediaScannerConnection
61     * when a connection to the MediaScanner service has been established
62     * and when the scanning of a file has completed.
63     */
64    public interface MediaScannerConnectionClient {
65        /**
66         * Called to notify the client when a connection to the
67         * MediaScanner service has been established.
68         */
69        public void onMediaScannerConnected();
70
71        /**
72         * Called to notify the client when the media scanner has finished
73         * scanning a file.
74         * @param path the path to the file that has been scanned.
75         * @param uri the Uri for the file if the scanning operation succeeded
76         * and the file was added to the media database, or null if scanning failed.
77         */
78        public void onScanCompleted(String path, Uri uri);
79    }
80
81    /**
82     * Constructs a new MediaScannerConnection object.
83     * @param context the Context object, required for establishing a connection to
84     * the media scanner service.
85     * @param client an optional object implementing the MediaScannerConnectionClient
86     * interface, for receiving notifications from the media scanner.
87     */
88    public MediaScannerConnection(Context context, MediaScannerConnectionClient client) {
89        mContext = context;
90        mClient = client;
91    }
92
93    /**
94     * Initiates a connection to the media scanner service.
95     * {@link MediaScannerConnectionClient#onMediaScannerConnected()}
96     * will be called when the connection is established.
97     */
98    public void connect() {
99        synchronized (this) {
100            if (!mConnected) {
101                Intent intent = new Intent(IMediaScannerService.class.getName());
102                mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
103                mConnected = true;
104            }
105        }
106    }
107
108    /**
109     * Releases the connection to the media scanner service.
110     */
111    public void disconnect() {
112        synchronized (this) {
113            if (mConnected) {
114                if (Config.LOGV) {
115                    Log.v(TAG, "Disconnecting from Media Scanner");
116                }
117                try {
118                    mContext.unbindService(this);
119                } catch (IllegalArgumentException ex) {
120                    if (Config.LOGV) {
121                        Log.v(TAG, "disconnect failed: " + ex);
122                    }
123                }
124                mConnected = false;
125            }
126        }
127    }
128
129    /**
130     * Returns whether we are connected to the media scanner service
131     * @return true if we are connected, false otherwise
132     */
133    public synchronized boolean isConnected() {
134        return (mService != null && mConnected);
135    }
136
137    /**
138     * Requests the media scanner to scan a file.
139     * @param path the path to the file to be scanned.
140     * @param mimeType  an optional mimeType for the file.
141     * If mimeType is null, then the mimeType will be inferred from the file extension.
142     * Success or failure of the scanning operation cannot be determined until
143     * {@link MediaScannerConnectionClient#onScanCompleted(String, Uri)} is called.
144     */
145     public void scanFile(String path, String mimeType) {
146        synchronized (this) {
147            if (mService == null || !mConnected) {
148                throw new IllegalStateException("not connected to MediaScannerService");
149            }
150            try {
151                if (Config.LOGV) {
152                    Log.v(TAG, "Scanning file " + path);
153                }
154                mService.requestScanFile(path, mimeType, mListener);
155            } catch (RemoteException e) {
156                if (Config.LOGD) {
157                    Log.d(TAG, "Failed to scan file " + path);
158                }
159            }
160        }
161    }
162
163    /**
164     * Part of the ServiceConnection interface.  Do not call.
165     */
166    public void onServiceConnected(ComponentName className, IBinder service) {
167        if (Config.LOGV) {
168            Log.v(TAG, "Connected to Media Scanner");
169        }
170        synchronized (this) {
171            mService = IMediaScannerService.Stub.asInterface(service);
172            if (mService != null && mClient != null) {
173                mClient.onMediaScannerConnected();
174            }
175        }
176    }
177
178    /**
179     * Part of the ServiceConnection interface.  Do not call.
180     */
181    public void onServiceDisconnected(ComponentName className) {
182        if (Config.LOGV) {
183            Log.v(TAG, "Disconnected from Media Scanner");
184        }
185        synchronized (this) {
186            mService = null;
187        }
188    }
189}
190