IngestService.java revision 1d6725c8074cc47949452a7236d37cbba8633585
1/* 2 * Copyright (C) 2013 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 com.android.gallery3d.ingest; 18 19import android.app.NotificationManager; 20import android.app.PendingIntent; 21import android.app.Service; 22import android.content.Context; 23import android.content.Intent; 24import android.media.MediaScannerConnection; 25import android.media.MediaScannerConnection.MediaScannerConnectionClient; 26import android.mtp.MtpDevice; 27import android.mtp.MtpDeviceInfo; 28import android.mtp.MtpObjectInfo; 29import android.net.Uri; 30import android.os.Binder; 31import android.os.IBinder; 32import android.os.SystemClock; 33import android.support.v4.app.NotificationCompat; 34import android.util.SparseBooleanArray; 35import android.widget.Adapter; 36 37import com.android.gallery3d.R; 38import com.android.gallery3d.app.NotificationIds; 39import com.android.gallery3d.data.MtpClient; 40import com.android.gallery3d.util.BucketNames; 41import com.android.gallery3d.util.UsageStatistics; 42 43import java.util.ArrayList; 44import java.util.Collection; 45import java.util.List; 46 47public class IngestService extends Service implements ImportTask.Listener, 48 MtpDeviceIndex.ProgressListener, MtpClient.Listener { 49 50 public class LocalBinder extends Binder { 51 IngestService getService() { 52 return IngestService.this; 53 } 54 } 55 56 private static final int PROGRESS_UPDATE_INTERVAL_MS = 180; 57 58 private static MtpClient sClient; 59 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; 75 76 @Override 77 public void onCreate() { 78 super.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); 86 87 if (sClient == null) { 88 sClient = new MtpClient(getApplicationContext()); 89 } 90 List<MtpDevice> devices = sClient.getDeviceList(); 91 if (devices.size() > 0) { 92 setDevice(devices.get(0)); 93 } 94 sClient.addListener(this); 95 } 96 97 @Override 98 public void onDestroy() { 99 sClient.removeListener(this); 100 mIndex.unsetProgressListener(this); 101 super.onDestroy(); 102 } 103 104 @Override 105 public IBinder onBind(Intent intent) { 106 return mBinder; 107 } 108 109 private void setDevice(MtpDevice device) { 110 if (mDevice == device) return; 111 mRedeliverImportFinish = false; 112 mRedeliverObjectsNotImported = null; 113 mRedeliverNotifyIndexChanged = false; 114 mRedeliverIndexFinish = false; 115 mDevice = device; 116 mIndex.setDevice(mDevice); 117 if (mDevice != null) { 118 MtpDeviceInfo deviceInfo = mDevice.getDeviceInfo(); 119 if (deviceInfo == null) { 120 setDevice(null); 121 return; 122 } else { 123 mDevicePrettyName = deviceInfo.getModel(); 124 mNotificationBuilder.setContentTitle(mDevicePrettyName); 125 new Thread(mIndex.getIndexRunnable()).start(); 126 } 127 } else { 128 mDevicePrettyName = null; 129 } 130 if (mClientActivity != null) { 131 mClientActivity.notifyIndexChanged(); 132 } else { 133 mRedeliverNotifyIndexChanged = true; 134 } 135 } 136 137 protected MtpDeviceIndex getIndex() { 138 return mIndex; 139 } 140 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()); 150 } 151 return; 152 } 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; 160 } 161 if (mRedeliverNotifyIndexChanged) { 162 mClientActivity.notifyIndexChanged(); 163 mRedeliverNotifyIndexChanged = false; 164 } 165 if (mRedeliverIndexFinish) { 166 mClientActivity.onIndexFinish(); 167 mRedeliverIndexFinish = false; 168 } 169 if (mDevice != null) { 170 mNeedRelaunchNotification = true; 171 } 172 } 173 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)); 181 } 182 } 183 } 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(); 191 } 192 193 @Override 194 public void deviceAdded(MtpDevice device) { 195 if (mDevice == null) { 196 setDevice(device); 197 UsageStatistics.onEvent(UsageStatistics.COMPONENT_IMPORTER, 198 "DeviceConnected", null); 199 } 200 } 201 202 @Override 203 public void deviceRemoved(MtpDevice device) { 204 if (device == mDevice) { 205 setDevice(null); 206 mNeedRelaunchNotification = false; 207 mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_SCANNING); 208 } 209 } 210 211 @Override 212 public void onImportProgress(int visitedCount, int totalCount, 213 String pathIfSuccessful) { 214 if (pathIfSuccessful != null) { 215 mScannerClient.scanPath(pathIfSuccessful); 216 } 217 mNeedRelaunchNotification = false; 218 if (mClientActivity != null) { 219 mClientActivity.onImportProgress(visitedCount, totalCount, pathIfSuccessful); 220 } 221 mNotificationBuilder.setProgress(totalCount, visitedCount, false) 222 .setContentText(getResources().getText(R.string.ingest_importing)); 223 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING, 224 mNotificationBuilder.build()); 225 } 226 227 @Override 228 public void onImportFinish(Collection<MtpObjectInfo> objectsNotImported, 229 int visitedCount) { 230 stopForeground(true); 231 mNeedRelaunchNotification = true; 232 if (mClientActivity != null) { 233 mClientActivity.onImportFinish(objectsNotImported, visitedCount); 234 } else { 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()); 242 } 243 UsageStatistics.onEvent(UsageStatistics.COMPONENT_IMPORTER, 244 "ImportFinished", null, visitedCount); 245 } 246 247 @Override 248 public void onObjectIndexed(MtpObjectInfo object, int numVisited) { 249 mNeedRelaunchNotification = false; 250 if (mClientActivity != null) { 251 mClientActivity.onObjectIndexed(object, numVisited); 252 } else { 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()); 261 } 262 } 263 } 264 265 @Override 266 public void onSorting() { 267 if (mClientActivity != null) mClientActivity.onSorting(); 268 } 269 270 @Override 271 public void onIndexFinish() { 272 mNeedRelaunchNotification = true; 273 if (mClientActivity != null) { 274 mClientActivity.onIndexFinish(); 275 } else { 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; 281 } 282 } 283 284 // Copied from old Gallery3d code 285 private static final class ScannerClient implements MediaScannerConnectionClient { 286 ArrayList<String> mPaths = new ArrayList<String>(); 287 MediaScannerConnection mScannerConnection; 288 boolean mConnected; 289 Object mLock = new Object(); 290 291 public ScannerClient(Context context) { 292 mScannerConnection = new MediaScannerConnection(context, this); 293 } 294 295 public void scanPath(String path) { 296 synchronized (mLock) { 297 if (mConnected) { 298 mScannerConnection.scanFile(path, null); 299 } else { 300 mPaths.add(path); 301 mScannerConnection.connect(); 302 } 303 } 304 } 305 306 @Override 307 public void onMediaScannerConnected() { 308 synchronized (mLock) { 309 mConnected = true; 310 if (!mPaths.isEmpty()) { 311 for (String path : mPaths) { 312 mScannerConnection.scanFile(path, null); 313 } 314 mPaths.clear(); 315 } 316 } 317 } 318 319 @Override 320 public void onScanCompleted(String path, Uri uri) { 321 } 322 } 323} 324