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.data;
18
19import android.annotation.TargetApi;
20import android.content.Context;
21import android.mtp.MtpDevice;
22import android.os.Build;
23import android.os.Environment;
24import android.os.PowerManager;
25import android.os.StatFs;
26import android.util.Log;
27
28import java.io.File;
29import java.util.Collection;
30import java.util.LinkedList;
31import java.util.List;
32
33/**
34 * Task that handles the copying of items from an MTP device.
35 */
36@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
37public class ImportTask implements Runnable {
38
39  private static final String TAG = "ImportTask";
40
41  /**
42   * Import progress listener.
43   */
44  public interface Listener {
45    void onImportProgress(int visitedCount, int totalCount, String pathIfSuccessful);
46
47    void onImportFinish(Collection<IngestObjectInfo> objectsNotImported, int visitedCount);
48  }
49
50  private static final String WAKELOCK_LABEL = "Google Photos MTP Import Task";
51
52  private Listener mListener;
53  private String mDestAlbumName;
54  private Collection<IngestObjectInfo> mObjectsToImport;
55  private MtpDevice mDevice;
56  private PowerManager.WakeLock mWakeLock;
57
58  public ImportTask(MtpDevice device, Collection<IngestObjectInfo> objectsToImport,
59      String destAlbumName, Context context) {
60    mDestAlbumName = destAlbumName;
61    mObjectsToImport = objectsToImport;
62    mDevice = device;
63    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
64    mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, WAKELOCK_LABEL);
65  }
66
67  public void setListener(Listener listener) {
68    mListener = listener;
69  }
70
71  @Override
72  public void run() {
73    mWakeLock.acquire();
74    try {
75      List<IngestObjectInfo> objectsNotImported = new LinkedList<IngestObjectInfo>();
76      int visited = 0;
77      int total = mObjectsToImport.size();
78      mListener.onImportProgress(visited, total, null);
79      File dest = new File(Environment.getExternalStorageDirectory(), mDestAlbumName);
80      dest.mkdirs();
81      for (IngestObjectInfo object : mObjectsToImport) {
82        visited++;
83        String importedPath = null;
84        if (hasSpaceForSize(object.getCompressedSize())) {
85          importedPath = new File(dest, object.getName(mDevice)).getAbsolutePath();
86          if (!mDevice.importFile(object.getObjectHandle(), importedPath)) {
87            importedPath = null;
88          }
89        }
90        if (importedPath == null) {
91          objectsNotImported.add(object);
92        }
93        if (mListener != null) {
94          mListener.onImportProgress(visited, total, importedPath);
95        }
96      }
97      if (mListener != null) {
98        mListener.onImportFinish(objectsNotImported, visited);
99      }
100    } finally {
101      mListener = null;
102      mWakeLock.release();
103    }
104  }
105
106  private static boolean hasSpaceForSize(long size) {
107    String state = Environment.getExternalStorageState();
108    if (!Environment.MEDIA_MOUNTED.equals(state)) {
109      return false;
110    }
111
112    String path = Environment.getExternalStorageDirectory().getPath();
113    try {
114      StatFs stat = new StatFs(path);
115      return stat.getAvailableBlocks() * (long) stat.getBlockSize() > size;
116    } catch (Exception e) {
117      Log.i(TAG, "Fail to access external storage", e);
118    }
119    return false;
120  }
121}
122