/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.gallery3d.ingest; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; import android.mtp.MtpDevice; import android.mtp.MtpDeviceInfo; import android.mtp.MtpObjectInfo; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.SystemClock; import android.support.v4.app.NotificationCompat; import android.util.SparseBooleanArray; import android.widget.Adapter; import com.android.gallery3d.R; import com.android.gallery3d.app.NotificationIds; import com.android.gallery3d.data.MtpClient; import com.android.gallery3d.ingest.data.MtpBitmapFetch; import com.android.gallery3d.util.BucketNames; import java.util.ArrayList; import java.util.Collection; import java.util.List; public class IngestService extends Service implements ImportTask.Listener, MtpDeviceIndex.ProgressListener, MtpClient.Listener { public class LocalBinder extends Binder { IngestService getService() { return IngestService.this; } } private static final int PROGRESS_UPDATE_INTERVAL_MS = 180; private static MtpClient sClient; private final IBinder mBinder = new LocalBinder(); private ScannerClient mScannerClient; private MtpDevice mDevice; private String mDevicePrettyName; private MtpDeviceIndex mIndex; private IngestActivity mClientActivity; private boolean mRedeliverImportFinish = false; private Collection mRedeliverObjectsNotImported; private boolean mRedeliverNotifyIndexChanged = false; private boolean mRedeliverIndexFinish = false; private NotificationManager mNotificationManager; private NotificationCompat.Builder mNotificationBuilder; private long mLastProgressIndexTime = 0; @Override public void onCreate() { super.onCreate(); mScannerClient = new ScannerClient(this); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mNotificationBuilder = new NotificationCompat.Builder(this); mNotificationBuilder.setSmallIcon(android.R.drawable.stat_notify_sync) // TODO drawable .setContentIntent(PendingIntent.getActivity(this, 0, new Intent(this, IngestActivity.class), 0)); mIndex = MtpDeviceIndex.getInstance(); mIndex.setProgressListener(this); if (sClient == null) { sClient = new MtpClient(getApplicationContext()); } List devices = sClient.getDeviceList(); if (devices.size() > 0) { setDevice(devices.get(0)); } sClient.addListener(this); } @Override public void onDestroy() { sClient.removeListener(this); mIndex.unsetProgressListener(this); super.onDestroy(); } @Override public IBinder onBind(Intent intent) { return mBinder; } private void setDevice(MtpDevice device) { if (mDevice == device) return; mRedeliverImportFinish = false; mRedeliverObjectsNotImported = null; mRedeliverNotifyIndexChanged = false; mRedeliverIndexFinish = false; mDevice = device; mIndex.setDevice(mDevice); if (mDevice != null) { MtpDeviceInfo deviceInfo = mDevice.getDeviceInfo(); if (deviceInfo == null) { setDevice(null); return; } else { mDevicePrettyName = deviceInfo.getModel(); mNotificationBuilder.setContentTitle(mDevicePrettyName); new Thread(mIndex.getIndexRunnable()).start(); } } else { mDevicePrettyName = null; } if (mClientActivity != null) { mClientActivity.notifyIndexChanged(); } else { mRedeliverNotifyIndexChanged = true; } } protected MtpDeviceIndex getIndex() { return mIndex; } protected void setClientActivity(IngestActivity activity) { if (mClientActivity == activity) return; mClientActivity = activity; if (mClientActivity == null) return; mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_IMPORTING); mNotificationManager.cancel(NotificationIds.INGEST_NOTIFICATION_SCANNING); if (mRedeliverImportFinish) { mClientActivity.onImportFinish(mRedeliverObjectsNotImported); mRedeliverImportFinish = false; mRedeliverObjectsNotImported = null; } if (mRedeliverNotifyIndexChanged) { mClientActivity.notifyIndexChanged(); mRedeliverNotifyIndexChanged = false; } if (mRedeliverIndexFinish) { mClientActivity.onIndexFinish(); mRedeliverIndexFinish = false; } } protected void importSelectedItems(SparseBooleanArray selected, Adapter adapter) { List importHandles = new ArrayList(); for (int i = 0; i < selected.size(); i++) { if (selected.valueAt(i)) { Object item = adapter.getItem(selected.keyAt(i)); if (item instanceof MtpObjectInfo) { importHandles.add(((MtpObjectInfo) item)); } } } ImportTask task = new ImportTask(mDevice, importHandles, BucketNames.IMPORTED, this); task.setListener(this); mNotificationBuilder.setProgress(0, 0, true) .setContentText(getResources().getText(R.string.ingest_importing)); startForeground(NotificationIds.INGEST_NOTIFICATION_IMPORTING, mNotificationBuilder.build()); new Thread(task).start(); } @Override public void deviceAdded(MtpDevice device) { if (mDevice == null) { setDevice(device); } } @Override public void deviceRemoved(MtpDevice device) { if (device == mDevice) { setDevice(null); } } @Override public void onImportProgress(int visitedCount, int totalCount, String pathIfSuccessful) { if (pathIfSuccessful != null) { mScannerClient.scanPath(pathIfSuccessful); } if (mClientActivity != null) { mClientActivity.onImportProgress(visitedCount, totalCount, pathIfSuccessful); } mNotificationBuilder.setProgress(totalCount, visitedCount, false) .setContentText(getResources().getText(R.string.ingest_importing)); mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING, mNotificationBuilder.build()); } @Override public void onImportFinish(Collection objectsNotImported) { stopForeground(true); if (mClientActivity != null) { mClientActivity.onImportFinish(objectsNotImported); } else { mRedeliverImportFinish = true; mRedeliverObjectsNotImported = objectsNotImported; mNotificationBuilder.setProgress(0, 0, false) .setContentText(getResources().getText(R.string.import_complete)); mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_IMPORTING, mNotificationBuilder.build()); } } @Override public void onObjectIndexed(MtpObjectInfo object, int numVisited) { if (mClientActivity != null) { mClientActivity.onObjectIndexed(object, numVisited); } else { // Throttle the updates to one every PROGRESS_UPDATE_INTERVAL_MS milliseconds long currentTime = SystemClock.uptimeMillis(); if (currentTime > mLastProgressIndexTime + PROGRESS_UPDATE_INTERVAL_MS) { mLastProgressIndexTime = currentTime; mNotificationBuilder.setProgress(0, numVisited, true) .setContentText(getResources().getText(R.string.ingest_scanning)); mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING, mNotificationBuilder.build()); } } } @Override public void onSorting() { if (mClientActivity != null) mClientActivity.onSorting(); } @Override public void onIndexFinish() { if (mClientActivity != null) { mClientActivity.onIndexFinish(); } else { mNotificationBuilder.setProgress(0, 0, false) .setContentText(getResources().getText(R.string.ingest_scanning_done)); mNotificationManager.notify(NotificationIds.INGEST_NOTIFICATION_SCANNING, mNotificationBuilder.build()); mRedeliverIndexFinish = true; } } // Copied from old Gallery3d code private static final class ScannerClient implements MediaScannerConnectionClient { ArrayList mPaths = new ArrayList(); MediaScannerConnection mScannerConnection; boolean mConnected; Object mLock = new Object(); public ScannerClient(Context context) { mScannerConnection = new MediaScannerConnection(context, this); } public void scanPath(String path) { synchronized (mLock) { if (mConnected) { mScannerConnection.scanFile(path, null); } else { mPaths.add(path); mScannerConnection.connect(); } } } @Override public void onMediaScannerConnected() { synchronized (mLock) { mConnected = true; if (!mPaths.isEmpty()) { for (String path : mPaths) { mScannerConnection.scanFile(path, null); } mPaths.clear(); } } } @Override public void onScanCompleted(String path, Uri uri) { } } }