IngestService.java revision f52ceba89962829aa12f5caba131580e8da85880
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.ingest.data.MtpBitmapFetch; 41import com.android.gallery3d.util.BucketNames; 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 Collection<MtpObjectInfo> mRedeliverObjectsNotImported; 68 private boolean mRedeliverNotifyIndexChanged = false; 69 private boolean mRedeliverIndexFinish = false; 70 private NotificationManager mNotificationManager; 71 private NotificationCompat.Builder mNotificationBuilder; 72 private long mLastProgressIndexTime = 0; 73 74 @Override 75 public void onCreate() { 76 super.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); 84 85 if (sClient == null) { 86 sClient = new MtpClient(getApplicationContext()); 87 } 88 List<MtpDevice> devices = sClient.getDeviceList(); 89 if (devices.size() > 0) { 90 setDevice(devices.get(0)); 91 } 92 sClient.addListener(this); 93 } 94 95 @Override 96 public void onDestroy() { 97 sClient.removeListener(this); 98 mIndex.unsetProgressListener(this); 99 super.onDestroy(); 100 } 101 102 @Override 103 public IBinder onBind(Intent intent) { 104 return mBinder; 105 } 106 107 private void setDevice(MtpDevice device) { 108 if (mDevice == device) return; 109 mRedeliverImportFinish = false; 110 mRedeliverObjectsNotImported = null; 111 mRedeliverNotifyIndexChanged = false; 112 mRedeliverIndexFinish = false; 113 mDevice = device; 114 mIndex.setDevice(mDevice); 115 if (mDevice != null) { 116 MtpDeviceInfo deviceInfo = mDevice.getDeviceInfo(); 117 if (deviceInfo == null) { 118 setDevice(null); 119 return; 120 } else { 121 mDevicePrettyName = deviceInfo.getModel(); 122 mNotificationBuilder.setContentTitle(mDevicePrettyName); 123 new Thread(mIndex.getIndexRunnable()).start(); 124 } 125 } else { 126 mDevicePrettyName = null; 127 } 128 if (mClientActivity != null) { 129 mClientActivity.notifyIndexChanged(); 130 } else { 131 mRedeliverNotifyIndexChanged = true; 132 } 133 } 134 135 protected MtpDeviceIndex getIndex() { 136 return mIndex; 137 } 138 139 protected void setClientActivity(IngestActivity activity) { 140 if (mClientActivity == activity) return; 141 mClientActivity = activity; 142 if (mClientActivity == null) return; 143 mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_IMPORTING); 144 mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_SCANNING); 145 if (mRedeliverImportFinish) { 146 mClientActivity.onImportFinish(mRedeliverObjectsNotImported); 147 mRedeliverImportFinish = false; 148 mRedeliverObjectsNotImported = null; 149 } 150 if (mRedeliverNotifyIndexChanged) { 151 mClientActivity.notifyIndexChanged(); 152 mRedeliverNotifyIndexChanged = false; 153 } 154 if (mRedeliverIndexFinish) { 155 mClientActivity.onIndexFinish(); 156 mRedeliverIndexFinish = false; 157 } 158 } 159 160 protected void importSelectedItems(SparseBooleanArray selected, Adapter adapter) { 161 List<MtpObjectInfo> importHandles = new ArrayList<MtpObjectInfo>(); 162 for (int i = 0; i < selected.size(); i++) { 163 if (selected.valueAt(i)) { 164 Object item = adapter.getItem(selected.keyAt(i)); 165 if (item instanceof MtpObjectInfo) { 166 importHandles.add(((MtpObjectInfo) item)); 167 } 168 } 169 } 170 ImportTask task = new ImportTask(mDevice, importHandles, BucketNames.IMPORTED, this); 171 task.setListener(this); 172 mNotificationBuilder.setProgress(0, 0, true) 173 .setContentText(getResources().getText(R.string.ingest_importing)); 174 startForeground(NotificationIds.INGEST_NOTIFICATION_IMPORTING, 175 mNotificationBuilder.build()); 176 new Thread(task).start(); 177 } 178 179 @Override 180 public void deviceAdded(MtpDevice device) { 181 if (mDevice == null) { 182 setDevice(device); 183 } 184 } 185 186 @Override 187 public void deviceRemoved(MtpDevice device) { 188 if (device == mDevice) { 189 setDevice(null); 190 } 191 } 192 193 @Override 194 public void onImportProgress(int visitedCount, int totalCount, 195 String pathIfSuccessful) { 196 if (pathIfSuccessful != null) { 197 mScannerClient.scanPath(pathIfSuccessful); 198 } 199 if (mClientActivity != null) { 200 mClientActivity.onImportProgress(visitedCount, totalCount, pathIfSuccessful); 201 } 202 mNotificationBuilder.setProgress(totalCount, visitedCount, false) 203 .setContentText(getResources().getText(R.string.ingest_importing)); 204 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING, 205 mNotificationBuilder.build()); 206 } 207 208 @Override 209 public void onImportFinish(Collection<MtpObjectInfo> objectsNotImported) { 210 stopForeground(true); 211 if (mClientActivity != null) { 212 mClientActivity.onImportFinish(objectsNotImported); 213 } else { 214 mRedeliverImportFinish = true; 215 mRedeliverObjectsNotImported = objectsNotImported; 216 mNotificationBuilder.setProgress(0, 0, false) 217 .setContentText(getResources().getText(R.string.import_complete)); 218 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING, 219 mNotificationBuilder.build()); 220 } 221 } 222 223 @Override 224 public void onObjectIndexed(MtpObjectInfo object, int numVisited) { 225 if (mClientActivity != null) { 226 mClientActivity.onObjectIndexed(object, numVisited); 227 } else { 228 // Throttle the updates to one every PROGRESS_UPDATE_INTERVAL_MS milliseconds 229 long currentTime = SystemClock.uptimeMillis(); 230 if (currentTime > mLastProgressIndexTime + PROGRESS_UPDATE_INTERVAL_MS) { 231 mLastProgressIndexTime = currentTime; 232 mNotificationBuilder.setProgress(0, numVisited, true) 233 .setContentText(getResources().getText(R.string.ingest_scanning)); 234 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING, 235 mNotificationBuilder.build()); 236 } 237 } 238 } 239 240 @Override 241 public void onSorting() { 242 if (mClientActivity != null) mClientActivity.onSorting(); 243 } 244 245 @Override 246 public void onIndexFinish() { 247 if (mClientActivity != null) { 248 mClientActivity.onIndexFinish(); 249 } else { 250 mNotificationBuilder.setProgress(0, 0, false) 251 .setContentText(getResources().getText(R.string.ingest_scanning_done)); 252 mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING, 253 mNotificationBuilder.build()); 254 mRedeliverIndexFinish = true; 255 } 256 } 257 258 // Copied from old Gallery3d code 259 private static final class ScannerClient implements MediaScannerConnectionClient { 260 ArrayList<String> mPaths = new ArrayList<String>(); 261 MediaScannerConnection mScannerConnection; 262 boolean mConnected; 263 Object mLock = new Object(); 264 265 public ScannerClient(Context context) { 266 mScannerConnection = new MediaScannerConnection(context, this); 267 } 268 269 public void scanPath(String path) { 270 synchronized (mLock) { 271 if (mConnected) { 272 mScannerConnection.scanFile(path, null); 273 } else { 274 mPaths.add(path); 275 mScannerConnection.connect(); 276 } 277 } 278 } 279 280 @Override 281 public void onMediaScannerConnected() { 282 synchronized (mLock) { 283 mConnected = true; 284 if (!mPaths.isEmpty()) { 285 for (String path : mPaths) { 286 mScannerConnection.scanFile(path, null); 287 } 288 mPaths.clear(); 289 } 290 } 291 } 292 293 @Override 294 public void onScanCompleted(String path, Uri uri) { 295 } 296 } 297} 298