Open, download and cancel operations linked to contextual menu for files
[pub/Android/ownCloud.git] / src / com / owncloud / android / operations / UploadFileOperation.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.File;
22 import java.io.IOException;
23 import java.util.HashSet;
24 import java.util.Set;
25 import java.util.concurrent.atomic.AtomicBoolean;
26
27 import org.apache.commons.httpclient.HttpException;
28 import org.apache.commons.httpclient.methods.PutMethod;
29 import org.apache.http.HttpStatus;
30
31 import com.owncloud.android.datamodel.OCFile;
32 import com.owncloud.android.operations.RemoteOperation;
33 import com.owncloud.android.operations.RemoteOperationResult;
34
35 import eu.alefzero.webdav.FileRequestEntity;
36 import eu.alefzero.webdav.OnDatatransferProgressListener;
37 import eu.alefzero.webdav.WebdavClient;
38 import eu.alefzero.webdav.WebdavUtils;
39 import android.accounts.Account;
40 import android.util.Log;
41
42 /**
43 * Remote operation performing the upload of a file to an ownCloud server
44 *
45 * @author David A. Velasco
46 */
47 public class UploadFileOperation extends RemoteOperation {
48
49 private static final String TAG = UploadFileOperation.class.getSimpleName();
50
51 private Account mAccount;
52 private OCFile mFile;
53 private String mRemotePath = null;
54 private boolean mIsInstant = false;
55 private boolean mRemoteFolderToBeCreated = false;
56 private boolean mForceOverwrite = false;
57 PutMethod mPutMethod = null;
58 private Set<OnDatatransferProgressListener> mDataTransferListeners = new HashSet<OnDatatransferProgressListener>();
59 private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false);
60
61 public UploadFileOperation( Account account,
62 OCFile file,
63 boolean isInstant,
64 boolean forceOverwrite) {
65 if (account == null)
66 throw new IllegalArgumentException("Illegal NULL account in UploadFileOperation creation");
67 if (file == null)
68 throw new IllegalArgumentException("Illegal NULL file in UploadFileOperation creation");
69 if (file.getStoragePath() == null || file.getStoragePath().length() <= 0 || !(new File(file.getStoragePath()).exists())) {
70 throw new IllegalArgumentException("Illegal file in UploadFileOperation; storage path invalid or file not found: " + file.getStoragePath());
71 }
72
73 mAccount = account;
74 mFile = file;
75 mRemotePath = file.getRemotePath();
76 mIsInstant = isInstant;
77 mForceOverwrite = forceOverwrite;
78 }
79
80
81 public Account getAccount() {
82 return mAccount;
83 }
84
85 public OCFile getFile() {
86 return mFile;
87 }
88
89 public String getStoragePath() {
90 return mFile.getStoragePath();
91 }
92
93 public String getRemotePath() {
94 //return mFile.getRemotePath(); // DON'T MAKE THIS ; the remotePath used can be different to mFile.getRemotePath() if mForceOverwrite is 'false'; see run(...)
95 return mRemotePath;
96 }
97
98 public String getMimeType() {
99 return mFile.getMimetype();
100 }
101
102 public boolean isInstant() {
103 return mIsInstant;
104 }
105
106 public boolean isRemoteFolderToBeCreated() {
107 return mRemoteFolderToBeCreated;
108 }
109
110 public void setRemoteFolderToBeCreated() {
111 mRemoteFolderToBeCreated = true;
112 }
113
114 public boolean getForceOverwrite() {
115 return mForceOverwrite;
116 }
117
118
119 public Set<OnDatatransferProgressListener> getDataTransferListeners() {
120 return mDataTransferListeners;
121 }
122
123 public void addDatatransferProgressListener (OnDatatransferProgressListener listener) {
124 mDataTransferListeners.add(listener);
125 }
126
127
128 @Override
129 protected RemoteOperationResult run(WebdavClient client) {
130 RemoteOperationResult result = null;
131 boolean nameCheckPassed = false;
132 try {
133 /// rename the file to upload, if necessary
134 if (!mForceOverwrite) {
135 mRemotePath = getAvailableRemotePath(client, mRemotePath);
136 }
137
138 /// perform the upload
139 nameCheckPassed = true;
140 synchronized(mCancellationRequested) {
141 if (mCancellationRequested.get()) {
142 throw new OperationCancelledException();
143 } else {
144 mPutMethod = new PutMethod(client.getBaseUri() + WebdavUtils.encodePath(mRemotePath));
145 }
146 }
147 int status = uploadFile(client);
148 result = new RemoteOperationResult(isSuccess(status), status);
149 Log.i(TAG, "Upload of " + mFile.getStoragePath() + " to " + mRemotePath + ": " + result.getLogMessage());
150
151 } catch (Exception e) {
152 // TODO something cleaner
153 if (mCancellationRequested.get()) {
154 result = new RemoteOperationResult(new OperationCancelledException());
155 } else {
156 result = new RemoteOperationResult(e);
157 }
158 Log.e(TAG, "Upload of " + mFile.getStoragePath() + " to " + mRemotePath + ": " + result.getLogMessage() + (nameCheckPassed?"":" (while checking file existence in server)"), result.getException());
159 }
160
161 return result;
162 }
163
164
165 public boolean isSuccess(int status) {
166 return ((status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED || status == HttpStatus.SC_NO_CONTENT));
167 }
168
169
170 protected int uploadFile(WebdavClient client) throws HttpException, IOException, OperationCancelledException {
171 int status = -1;
172 try {
173 File f = new File(mFile.getStoragePath());
174 FileRequestEntity entity = new FileRequestEntity(f, getMimeType());
175 entity.addOnDatatransferProgressListeners(mDataTransferListeners);
176 mPutMethod.setRequestEntity(entity);
177 status = client.executeMethod(mPutMethod);
178 client.exhaustResponse(mPutMethod.getResponseBodyAsStream());
179
180 } finally {
181 mPutMethod.releaseConnection(); // let the connection available for other methods
182 }
183 return status;
184 }
185
186 /**
187 * Checks if remotePath does not exist in the server and returns it, or adds a suffix to it in order to avoid the server
188 * file is overwritten.
189 *
190 * @param string
191 * @return
192 */
193 private String getAvailableRemotePath(WebdavClient wc, String remotePath) throws Exception {
194 boolean check = wc.existsFile(remotePath);
195 if (!check) {
196 return remotePath;
197 }
198
199 int pos = remotePath.lastIndexOf(".");
200 String suffix = "";
201 String extension = "";
202 if (pos >= 0) {
203 extension = remotePath.substring(pos+1);
204 remotePath = remotePath.substring(0, pos);
205 }
206 int count = 2;
207 do {
208 suffix = " (" + count + ")";
209 if (pos >= 0)
210 check = wc.existsFile(remotePath + suffix + "." + extension);
211 else
212 check = wc.existsFile(remotePath + suffix);
213 count++;
214 } while (check);
215
216 if (pos >=0) {
217 return remotePath + suffix + "." + extension;
218 } else {
219 return remotePath + suffix;
220 }
221 }
222
223
224 public void cancel() {
225 synchronized(mCancellationRequested) {
226 mCancellationRequested.set(true);
227 if (mPutMethod != null)
228 mPutMethod.abort();
229 }
230 }
231
232
233 }