2 * Copyright (C) 2013 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.gallery3d.ingest;
19 import android.app.NotificationManager;
20 import android.app.PendingIntent;
21 import android.app.Service;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.media.MediaScannerConnection;
25 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
26 import android.mtp.MtpDevice;
27 import android.mtp.MtpDeviceInfo;
28 import android.mtp.MtpObjectInfo;
29 import android.net.Uri;
30 import android.os.Binder;
31 import android.os.IBinder;
32 import android.os.SystemClock;
33 import android.support.v4.app.NotificationCompat;
34 import android.util.SparseBooleanArray;
35 import android.widget.Adapter;
37 import com.android.gallery3d.R;
38 import com.android.gallery3d.app.NotificationIds;
39 import com.android.gallery3d.data.MtpClient;
40 import com.android.gallery3d.util.BucketNames;
42 import java.util.ArrayList;
43 import java.util.Collection;
44 import java.util.List;
46 public class IngestService extends Service implements ImportTask.Listener,
47 MtpDeviceIndex.ProgressListener, MtpClient.Listener {
49 public class LocalBinder extends Binder {
50 IngestService getService() {
51 return IngestService.this;
55 private static final int PROGRESS_UPDATE_INTERVAL_MS = 180;
57 private static MtpClient sClient;
59 private final IBinder mBinder = new LocalBinder();
60 private ScannerClient mScannerClient;
61 private MtpDevice mDevice;
62 private String mDevicePrettyName;
63 private MtpDeviceIndex mIndex;
64 private IngestActivity mClientActivity;
65 private boolean mRedeliverImportFinish = false;
66 private Collection<MtpObjectInfo> mRedeliverObjectsNotImported;
67 private boolean mRedeliverNotifyIndexChanged = false;
68 private boolean mRedeliverIndexFinish = false;
69 private NotificationManager mNotificationManager;
70 private NotificationCompat.Builder mNotificationBuilder;
71 private long mLastProgressIndexTime = 0;
72 private boolean mNeedRelaunchNotification = false;
75 public void onCreate() {
77 mScannerClient = new ScannerClient(this);
78 mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
79 mNotificationBuilder = new NotificationCompat.Builder(this);
80 mNotificationBuilder.setSmallIcon(android.R.drawable.stat_notify_sync) // TODO drawable
81 .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, IngestActivity.class), 0));
82 mIndex = MtpDeviceIndex.getInstance();
83 mIndex.setProgressListener(this);
85 if (sClient == null) {
86 sClient = new MtpClient(getApplicationContext());
88 List<MtpDevice> devices = sClient.getDeviceList();
89 if (devices.size() > 0) {
90 setDevice(devices.get(0));
92 sClient.addListener(this);
96 public void onDestroy() {
97 sClient.removeListener(this);
98 mIndex.unsetProgressListener(this);
103 public IBinder onBind(Intent intent) {
107 private void setDevice(MtpDevice device) {
108 if (mDevice == device) return;
109 mRedeliverImportFinish = false;
110 mRedeliverObjectsNotImported = null;
111 mRedeliverNotifyIndexChanged = false;
112 mRedeliverIndexFinish = false;
114 mIndex.setDevice(mDevice);
115 if (mDevice != null) {
116 MtpDeviceInfo deviceInfo = mDevice.getDeviceInfo();
117 if (deviceInfo == null) {
121 mDevicePrettyName = deviceInfo.getModel();
122 mNotificationBuilder.setContentTitle(mDevicePrettyName);
123 new Thread(mIndex.getIndexRunnable()).start();
126 mDevicePrettyName = null;
128 if (mClientActivity != null) {
129 mClientActivity.notifyIndexChanged();
131 mRedeliverNotifyIndexChanged = true;
135 protected MtpDeviceIndex getIndex() {
139 protected void setClientActivity(IngestActivity activity) {
140 if (mClientActivity == activity) return;
141 mClientActivity = activity;
142 if (mClientActivity == null) {
143 if (mNeedRelaunchNotification) {
144 mNotificationBuilder.setProgress(0, 0, false)
145 .setContentText(getResources().getText(R.string.ingest_scanning_done));
146 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING,
147 mNotificationBuilder.build());
151 mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_IMPORTING);
152 mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_SCANNING);
153 if (mRedeliverImportFinish) {
154 mClientActivity.onImportFinish(mRedeliverObjectsNotImported);
155 mRedeliverImportFinish = false;
156 mRedeliverObjectsNotImported = null;
158 if (mRedeliverNotifyIndexChanged) {
159 mClientActivity.notifyIndexChanged();
160 mRedeliverNotifyIndexChanged = false;
162 if (mRedeliverIndexFinish) {
163 mClientActivity.onIndexFinish();
164 mRedeliverIndexFinish = false;
168 protected void importSelectedItems(SparseBooleanArray selected, Adapter adapter) {
169 List<MtpObjectInfo> importHandles = new ArrayList<MtpObjectInfo>();
170 for (int i = 0; i < selected.size(); i++) {
171 if (selected.valueAt(i)) {
172 Object item = adapter.getItem(selected.keyAt(i));
173 if (item instanceof MtpObjectInfo) {
174 importHandles.add(((MtpObjectInfo) item));
178 ImportTask task = new ImportTask(mDevice, importHandles, BucketNames.IMPORTED, this);
179 task.setListener(this);
180 mNotificationBuilder.setProgress(0, 0, true)
181 .setContentText(getResources().getText(R.string.ingest_importing));
182 startForeground(NotificationIds.INGEST_NOTIFICATION_IMPORTING,
183 mNotificationBuilder.build());
184 new Thread(task).start();
188 public void deviceAdded(MtpDevice device) {
189 if (mDevice == null) {
195 public void deviceRemoved(MtpDevice device) {
196 if (device == mDevice) {
198 mNeedRelaunchNotification = false;
203 public void onImportProgress(int visitedCount, int totalCount,
204 String pathIfSuccessful) {
205 if (pathIfSuccessful != null) {
206 mScannerClient.scanPath(pathIfSuccessful);
208 mNeedRelaunchNotification = false;
209 if (mClientActivity != null) {
210 mClientActivity.onImportProgress(visitedCount, totalCount, pathIfSuccessful);
212 mNotificationBuilder.setProgress(totalCount, visitedCount, false)
213 .setContentText(getResources().getText(R.string.ingest_importing));
214 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING,
215 mNotificationBuilder.build());
219 public void onImportFinish(Collection<MtpObjectInfo> objectsNotImported) {
220 stopForeground(true);
221 mNeedRelaunchNotification = true;
222 if (mClientActivity != null) {
223 mClientActivity.onImportFinish(objectsNotImported);
225 mRedeliverImportFinish = true;
226 mRedeliverObjectsNotImported = objectsNotImported;
227 mNotificationBuilder.setProgress(0, 0, false)
228 .setContentText(getResources().getText(R.string.import_complete));
229 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING,
230 mNotificationBuilder.build());
235 public void onObjectIndexed(MtpObjectInfo object, int numVisited) {
236 mNeedRelaunchNotification = false;
237 if (mClientActivity != null) {
238 mClientActivity.onObjectIndexed(object, numVisited);
240 // Throttle the updates to one every PROGRESS_UPDATE_INTERVAL_MS milliseconds
241 long currentTime = SystemClock.uptimeMillis();
242 if (currentTime > mLastProgressIndexTime + PROGRESS_UPDATE_INTERVAL_MS) {
243 mLastProgressIndexTime = currentTime;
244 mNotificationBuilder.setProgress(0, numVisited, true)
245 .setContentText(getResources().getText(R.string.ingest_scanning));
246 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING,
247 mNotificationBuilder.build());
253 public void onSorting() {
254 if (mClientActivity != null) mClientActivity.onSorting();
258 public void onIndexFinish() {
259 mNeedRelaunchNotification = true;
260 if (mClientActivity != null) {
261 mClientActivity.onIndexFinish();
263 mNotificationBuilder.setProgress(0, 0, false)
264 .setContentText(getResources().getText(R.string.ingest_scanning_done));
265 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING,
266 mNotificationBuilder.build());
267 mRedeliverIndexFinish = true;
271 // Copied from old Gallery3d code
272 private static final class ScannerClient implements MediaScannerConnectionClient {
273 ArrayList<String> mPaths = new ArrayList<String>();
274 MediaScannerConnection mScannerConnection;
276 Object mLock = new Object();
278 public ScannerClient(Context context) {
279 mScannerConnection = new MediaScannerConnection(context, this);
282 public void scanPath(String path) {
283 synchronized (mLock) {
285 mScannerConnection.scanFile(path, null);
288 mScannerConnection.connect();
294 public void onMediaScannerConnected() {
295 synchronized (mLock) {
297 if (!mPaths.isEmpty()) {
298 for (String path : mPaths) {
299 mScannerConnection.scanFile(path, null);
307 public void onScanCompleted(String path, Uri uri) {