Show the details of a file when the status notification of a download in progress...
[pub/Android/ownCloud.git] / src / com / owncloud / android / operations / DownloadFileOperation.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 package com.owncloud.android.operations;
20
21 import java.io.BufferedInputStream;
22 import java.io.File;
23 import java.io.FileOutputStream;
24 import java.io.IOException;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.Set;
28 import java.util.concurrent.atomic.AtomicBoolean;
29
30 import org.apache.commons.httpclient.HttpException;
31 import org.apache.commons.httpclient.methods.GetMethod;
32 import org.apache.http.HttpStatus;
33
34 import com.owncloud.android.datamodel.OCFile;
35 import com.owncloud.android.files.services.FileDownloader;
36 import com.owncloud.android.operations.RemoteOperation;
37 import com.owncloud.android.operations.RemoteOperationResult;
38
39 import eu.alefzero.webdav.OnDatatransferProgressListener;
40 import eu.alefzero.webdav.WebdavClient;
41 import eu.alefzero.webdav.WebdavUtils;
42 import android.accounts.Account;
43 import android.util.Log;
44 import android.webkit.MimeTypeMap;
45
46 /**
47 * Remote operation performing the download of a file to an ownCloud server
48 *
49 * @author David A. Velasco
50 */
51 public class DownloadFileOperation extends RemoteOperation {
52
53 private static final String TAG = DownloadFileOperation.class.getCanonicalName();
54
55 private Account mAccount = null;
56 private OCFile mFile;
57 private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
58
59 private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
60
61
62 public DownloadFileOperation(Account account, OCFile file) {
63 if (account == null)
64 throw new IllegalArgumentException("Illegal null account in DownloadFileOperation creation");
65 if (file == null)
66 throw new IllegalArgumentException("Illegal null file in DownloadFileOperation creation");
67
68 mAccount = account;
69 mFile = file;
70 }
71
72
73 public Account getAccount() {
74 return mAccount;
75 }
76
77 public OCFile getFile() {
78 return mFile;
79 }
80
81 public String getSavePath() {
82 return FileDownloader.getSavePath(mAccount.name) + mFile.getRemotePath();
83 }
84
85 public String getTmpPath() {
86 return FileDownloader.getTemporalPath(mAccount.name) + mFile.getRemotePath();
87 }
88
89 public String getRemotePath() {
90 return mFile.getRemotePath();
91 }
92
93 public String getMimeType() {
94 String mimeType = mFile.getMimetype();
95 if (mimeType == null || mimeType.length() <= 0) {
96 try {
97 mimeType = MimeTypeMap.getSingleton()
98 .getMimeTypeFromExtension(
99 mFile.getRemotePath().substring(mFile.getRemotePath().lastIndexOf('.') + 1));
100 } catch (IndexOutOfBoundsException e) {
101 Log.e(TAG, "Trying to find out MIME type of a file without extension: " + mFile.getRemotePath());
102 }
103 }
104 if (mimeType == null) {
105 mimeType = "application/octet-stream";
106 }
107 return mimeType;
108 }
109
110 public long getSize() {
111 return mFile.getFileLength();
112 }
113
114
115 public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
116 mDataTransferListeners.add(listener);
117 }
118
119 @Override
120 protected RemoteOperationResult run(WebdavClient client) {
121 RemoteOperationResult result = null;
122 File newFile = null;
123 boolean moved = false;
124
125 /// download will be performed to a temporal file, then moved to the final location
126 File tmpFile = new File(getTmpPath());
127
128 /// perform the download
129 try {
130 tmpFile.getParentFile().mkdirs();
131 int status = downloadFile(client, tmpFile);
132 if (isSuccess(status)) {
133 newFile = new File(getSavePath());
134 newFile.getParentFile().mkdirs();
135 moved = tmpFile.renameTo(newFile);
136 }
137 if (!moved)
138 result = new RemoteOperationResult(RemoteOperationResult.ResultCode.STORAGE_ERROR_MOVING_FROM_TMP);
139 else
140 result = new RemoteOperationResult(isSuccess(status), status);
141 Log.i(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage());
142
143 } catch (Exception e) {
144 result = new RemoteOperationResult(e);
145 Log.e(TAG, "Download of " + mFile.getRemotePath() + " to " + getSavePath() + ": " + result.getLogMessage(), e);
146 }
147
148 return result;
149 }
150
151
152 public boolean isSuccess(int status) {
153 return (status == HttpStatus.SC_OK);
154 }
155
156
157 protected int downloadFile(WebdavClient client, File targetFile) throws HttpException, IOException, OperationCancelledException {
158 int status = -1;
159 boolean savedFile = false;
160 GetMethod get = new GetMethod(client.getBaseUri() + WebdavUtils.encodePath(mFile.getRemotePath()));
161 Iterator<OnDatatransferProgressListener> it = null;
162
163 FileOutputStream fos = null;
164 try {
165 status = client.executeMethod(get);
166 if (isSuccess(status)) {
167 targetFile.createNewFile();
168 BufferedInputStream bis = new BufferedInputStream(get.getResponseBodyAsStream());
169 fos = new FileOutputStream(targetFile);
170 long transferred = 0;
171
172 byte[] bytes = new byte[4096];
173 int readResult = 0;
174 while ((readResult = bis.read(bytes)) != -1) {
175 synchronized(mCancellationRequested) {
176 if (mCancellationRequested.get()) {
177 get.abort();
178 throw new OperationCancelledException();
179 }
180 }
181 fos.write(bytes, 0, readResult);
182 transferred += readResult;
183 it = mDataTransferListeners.iterator();
184 while (it.hasNext()) {
185 it.next().onTransferProgress(readResult, transferred, mFile.getFileLength(), targetFile.getName());
186 }
187 }
188 savedFile = true;
189
190 } else {
191 client.exhaustResponse(get.getResponseBodyAsStream());
192 }
193
194 } finally {
195 if (fos != null) fos.close();
196 if (!savedFile && targetFile.exists()) {
197 targetFile.delete();
198 }
199 get.releaseConnection(); // let the connection available for other methods
200 }
201 return status;
202 }
203
204
205 public void cancel() {
206 mCancellationRequested.set(true); // atomic set; there is no need of synchronizing it
207 }
208
209 }