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;
41 import com.android.gallery3d.util.UsageStatistics;
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.List;
47 public class IngestService extends Service implements ImportTask.Listener,
48 MtpDeviceIndex.ProgressListener, MtpClient.Listener {
50 public class LocalBinder extends Binder {
51 IngestService getService() {
52 return IngestService.this;
56 private static final int PROGRESS_UPDATE_INTERVAL_MS = 180;
58 private static MtpClient sClient;
60 private final IBinder mBinder = new LocalBinder();
61 private ScannerClient mScannerClient;
62 private MtpDevice mDevice;
63 private String mDevicePrettyName;
64 private MtpDeviceIndex mIndex;
65 private IngestActivity mClientActivity;
66 private boolean mRedeliverImportFinish = false;
67 private int mRedeliverImportFinishCount = 0;
68 private Collection<MtpObjectInfo> mRedeliverObjectsNotImported;
69 private boolean mRedeliverNotifyIndexChanged = false;
70 private boolean mRedeliverIndexFinish = false;
71 private NotificationManager mNotificationManager;
72 private NotificationCompat.Builder mNotificationBuilder;
73 private long mLastProgressIndexTime = 0;
74 private boolean mNeedRelaunchNotification = false;
77 public void onCreate() {
79 mScannerClient = new ScannerClient(this);
80 mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
81 mNotificationBuilder = new NotificationCompat.Builder(this);
82 mNotificationBuilder.setSmallIcon(android.R.drawable.stat_notify_sync) // TODO drawable
83 .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, IngestActivity.class), 0));
84 mIndex = MtpDeviceIndex.getInstance();
85 mIndex.setProgressListener(this);
87 if (sClient == null) {
88 sClient = new MtpClient(getApplicationContext());
90 List<MtpDevice> devices = sClient.getDeviceList();
91 if (devices.size() > 0) {
92 setDevice(devices.get(0));
94 sClient.addListener(this);
98 public void onDestroy() {
99 sClient.removeListener(this);
100 mIndex.unsetProgressListener(this);
105 public IBinder onBind(Intent intent) {
109 private void setDevice(MtpDevice device) {
110 if (mDevice == device) return;
111 mRedeliverImportFinish = false;
112 mRedeliverObjectsNotImported = null;
113 mRedeliverNotifyIndexChanged = false;
114 mRedeliverIndexFinish = false;
116 mIndex.setDevice(mDevice);
117 if (mDevice != null) {
118 MtpDeviceInfo deviceInfo = mDevice.getDeviceInfo();
119 if (deviceInfo == null) {
123 mDevicePrettyName = deviceInfo.getModel();
124 mNotificationBuilder.setContentTitle(mDevicePrettyName);
125 new Thread(mIndex.getIndexRunnable()).start();
128 mDevicePrettyName = null;
130 if (mClientActivity != null) {
131 mClientActivity.notifyIndexChanged();
133 mRedeliverNotifyIndexChanged = true;
137 protected MtpDeviceIndex getIndex() {
141 protected void setClientActivity(IngestActivity activity) {
142 if (mClientActivity == activity) return;
143 mClientActivity = activity;
144 if (mClientActivity == null) {
145 if (mNeedRelaunchNotification) {
146 mNotificationBuilder.setProgress(0, 0, false)
147 .setContentText(getResources().getText(R.string.ingest_scanning_done));
148 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING,
149 mNotificationBuilder.build());
153 mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_IMPORTING);
154 mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_SCANNING);
155 if (mRedeliverImportFinish) {
156 mClientActivity.onImportFinish(mRedeliverObjectsNotImported,
157 mRedeliverImportFinishCount);
158 mRedeliverImportFinish = false;
159 mRedeliverObjectsNotImported = null;
161 if (mRedeliverNotifyIndexChanged) {
162 mClientActivity.notifyIndexChanged();
163 mRedeliverNotifyIndexChanged = false;
165 if (mRedeliverIndexFinish) {
166 mClientActivity.onIndexFinish();
167 mRedeliverIndexFinish = false;
169 if (mDevice != null) {
170 mNeedRelaunchNotification = true;
174 protected void importSelectedItems(SparseBooleanArray selected, Adapter adapter) {
175 List<MtpObjectInfo> importHandles = new ArrayList<MtpObjectInfo>();
176 for (int i = 0; i < selected.size(); i++) {
177 if (selected.valueAt(i)) {
178 Object item = adapter.getItem(selected.keyAt(i));
179 if (item instanceof MtpObjectInfo) {
180 importHandles.add(((MtpObjectInfo) item));
184 ImportTask task = new ImportTask(mDevice, importHandles, BucketNames.IMPORTED, this);
185 task.setListener(this);
186 mNotificationBuilder.setProgress(0, 0, true)
187 .setContentText(getResources().getText(R.string.ingest_importing));
188 startForeground(NotificationIds.INGEST_NOTIFICATION_IMPORTING,
189 mNotificationBuilder.build());
190 new Thread(task).start();
194 public void deviceAdded(MtpDevice device) {
195 if (mDevice == null) {
197 UsageStatistics.onEvent(UsageStatistics.COMPONENT_IMPORTER,
198 "DeviceConnected", null);
203 public void deviceRemoved(MtpDevice device) {
204 if (device == mDevice) {
206 mNeedRelaunchNotification = false;
207 mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_SCANNING);
212 public void onImportProgress(int visitedCount, int totalCount,
213 String pathIfSuccessful) {
214 if (pathIfSuccessful != null) {
215 mScannerClient.scanPath(pathIfSuccessful);
217 mNeedRelaunchNotification = false;
218 if (mClientActivity != null) {
219 mClientActivity.onImportProgress(visitedCount, totalCount, pathIfSuccessful);
221 mNotificationBuilder.setProgress(totalCount, visitedCount, false)
222 .setContentText(getResources().getText(R.string.ingest_importing));
223 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING,
224 mNotificationBuilder.build());
228 public void onImportFinish(Collection<MtpObjectInfo> objectsNotImported,
230 stopForeground(true);
231 mNeedRelaunchNotification = true;
232 if (mClientActivity != null) {
233 mClientActivity.onImportFinish(objectsNotImported, visitedCount);
235 mRedeliverImportFinish = true;
236 mRedeliverObjectsNotImported = objectsNotImported;
237 mRedeliverImportFinishCount = visitedCount;
238 mNotificationBuilder.setProgress(0, 0, false)
239 .setContentText(getResources().getText(R.string.import_complete));
240 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING,
241 mNotificationBuilder.build());
243 UsageStatistics.onEvent(UsageStatistics.COMPONENT_IMPORTER,
244 "ImportFinished", null, visitedCount);
248 public void onObjectIndexed(MtpObjectInfo object, int numVisited) {
249 mNeedRelaunchNotification = false;
250 if (mClientActivity != null) {
251 mClientActivity.onObjectIndexed(object, numVisited);
253 // Throttle the updates to one every PROGRESS_UPDATE_INTERVAL_MS milliseconds
254 long currentTime = SystemClock.uptimeMillis();
255 if (currentTime > mLastProgressIndexTime + PROGRESS_UPDATE_INTERVAL_MS) {
256 mLastProgressIndexTime = currentTime;
257 mNotificationBuilder.setProgress(0, numVisited, true)
258 .setContentText(getResources().getText(R.string.ingest_scanning));
259 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING,
260 mNotificationBuilder.build());
266 public void onSorting() {
267 if (mClientActivity != null) mClientActivity.onSorting();
271 public void onIndexFinish() {
272 mNeedRelaunchNotification = true;
273 if (mClientActivity != null) {
274 mClientActivity.onIndexFinish();
276 mNotificationBuilder.setProgress(0, 0, false)
277 .setContentText(getResources().getText(R.string.ingest_scanning_done));
278 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING,
279 mNotificationBuilder.build());
280 mRedeliverIndexFinish = true;
284 // Copied from old Gallery3d code
285 private static final class ScannerClient implements MediaScannerConnectionClient {
286 ArrayList<String> mPaths = new ArrayList<String>();
287 MediaScannerConnection mScannerConnection;
289 Object mLock = new Object();
291 public ScannerClient(Context context) {
292 mScannerConnection = new MediaScannerConnection(context, this);
295 public void scanPath(String path) {
296 synchronized (mLock) {
298 mScannerConnection.scanFile(path, null);
301 mScannerConnection.connect();
307 public void onMediaScannerConnected() {
308 synchronized (mLock) {
310 if (!mPaths.isEmpty()) {
311 for (String path : mPaths) {
312 mScannerConnection.scanFile(path, null);
320 public void onScanCompleted(String path, Uri uri) {