Home | History | Annotate | Download | only in ingest
      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 
     17 package com.android.gallery3d.ingest;
     18 
     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;
     36 
     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;
     42 
     43 import java.util.ArrayList;
     44 import java.util.Collection;
     45 import java.util.List;
     46 
     47 public 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