Merge branch 'master' of https://github.com/owncloud/android into material_fab
[pub/Android/ownCloud.git] / src / com / owncloud / android / files / FileOperationsHelper.java
1 /**
2 * ownCloud Android client application
3 *
4 * @author masensio
5 * @author David A. Velasco
6 * Copyright (C) 2015 ownCloud Inc.
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2,
10 * as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 package com.owncloud.android.files;
23
24 import android.accounts.Account;
25 import android.content.ActivityNotFoundException;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.pm.PackageManager;
29 import android.content.pm.ResolveInfo;
30 import android.net.Uri;
31 import android.support.v4.app.DialogFragment;
32 import android.webkit.MimeTypeMap;
33 import android.widget.Toast;
34
35 import com.owncloud.android.R;
36 import com.owncloud.android.authentication.AccountUtils;
37 import com.owncloud.android.datamodel.OCFile;
38 import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder;
39 import com.owncloud.android.files.services.FileUploader.FileUploaderBinder;
40 import com.owncloud.android.lib.common.network.WebdavUtils;
41 import com.owncloud.android.lib.common.utils.Log_OC;
42 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
43 import com.owncloud.android.services.OperationsService;
44 import com.owncloud.android.services.observer.FileObserverService;
45 import com.owncloud.android.ui.activity.FileActivity;
46 import com.owncloud.android.ui.dialog.ShareLinkToDialog;
47
48 import org.apache.http.protocol.HTTP;
49
50 import java.util.List;
51
52 /**
53 *
54 */
55 public class FileOperationsHelper {
56
57 private static final String TAG = FileOperationsHelper.class.getName();
58
59 private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
60
61 protected FileActivity mFileActivity = null;
62
63 /// Identifier of operation in progress which result shouldn't be lost
64 private long mWaitingForOpId = Long.MAX_VALUE;
65
66 public FileOperationsHelper(FileActivity fileActivity) {
67 mFileActivity = fileActivity;
68 }
69
70
71 public void openFile(OCFile file) {
72 if (file != null) {
73 String storagePath = file.getStoragePath();
74 String encodedStoragePath = WebdavUtils.encodePath(storagePath);
75
76 Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
77 intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), file.getMimetype());
78 intentForSavedMimeType.setFlags(
79 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
80 );
81
82 Intent intentForGuessedMimeType = null;
83 if (storagePath.lastIndexOf('.') >= 0) {
84 String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
85 storagePath.substring(storagePath.lastIndexOf('.') + 1)
86 );
87 if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
88 intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
89 intentForGuessedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
90 intentForGuessedMimeType.setFlags(
91 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
92 );
93 }
94 }
95
96 Intent openFileWithIntent;
97 if (intentForGuessedMimeType != null) {
98 openFileWithIntent = intentForGuessedMimeType;
99 } else {
100 openFileWithIntent = intentForSavedMimeType;
101 }
102
103 List<ResolveInfo> launchables = mFileActivity.getPackageManager().
104 queryIntentActivities(openFileWithIntent, PackageManager.GET_INTENT_FILTERS);
105
106 if(launchables != null && launchables.size() > 0) {
107 try {
108 mFileActivity.startActivity(
109 Intent.createChooser(
110 openFileWithIntent, mFileActivity.getString(R.string.actionbar_open_with)
111 )
112 );
113 } catch (ActivityNotFoundException anfe) {
114 showNoAppForFileTypeToast(mFileActivity.getApplicationContext());
115 }
116 } else {
117 showNoAppForFileTypeToast(mFileActivity.getApplicationContext());
118 }
119
120 } else {
121 Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
122 }
123 }
124
125 /**
126 * Displays a toast stating that no application could be found to open the file.
127 *
128 * @param context the context to be able to show a toast.
129 */
130 private void showNoAppForFileTypeToast(Context context) {
131 Toast.makeText(context,
132 R.string.file_list_no_app_for_file_type, Toast.LENGTH_SHORT)
133 .show();
134 }
135
136 public void shareFileWithLink(OCFile file) {
137
138 if (isSharedSupported()) {
139 if (file != null) {
140 String link = "https://fake.url";
141 Intent intent = createShareWithLinkIntent(link);
142 String[] packagesToExclude = new String[]{mFileActivity.getPackageName()};
143 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intent, packagesToExclude, file);
144 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
145
146 } else {
147 Log_OC.wtf(TAG, "Trying to share a NULL OCFile");
148 }
149
150 } else {
151 // Show a Message
152 Toast t = Toast.makeText(
153 mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG
154 );
155 t.show();
156 }
157 }
158
159
160 public void shareFileWithLinkToApp(OCFile file, String password, Intent sendIntent) {
161
162 if (file != null) {
163 mFileActivity.showLoadingDialog();
164
165 Intent service = new Intent(mFileActivity, OperationsService.class);
166 service.setAction(OperationsService.ACTION_CREATE_SHARE);
167 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
168 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
169 service.putExtra(OperationsService.EXTRA_PASSWORD_SHARE, password);
170 service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent);
171 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
172
173 } else {
174 Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
175 }
176 }
177
178
179 private Intent createShareWithLinkIntent(String link) {
180 Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
181 intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
182 intentToShareLink.setType(HTTP.PLAIN_TEXT_TYPE);
183 return intentToShareLink;
184 }
185
186
187 /**
188 * @return 'True' if the server supports the Share API
189 */
190 public boolean isSharedSupported() {
191 if (mFileActivity.getAccount() != null) {
192 OwnCloudVersion serverVersion = AccountUtils.getServerVersion(mFileActivity.getAccount());
193 return (serverVersion != null && serverVersion.isSharedSupported());
194 }
195 return false;
196 }
197
198
199 public void unshareFileWithLink(OCFile file) {
200
201 if (isSharedSupported()) {
202 // Unshare the file
203 Intent service = new Intent(mFileActivity, OperationsService.class);
204 service.setAction(OperationsService.ACTION_UNSHARE);
205 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
206 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
207 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
208
209 mFileActivity.showLoadingDialog();
210
211 } else {
212 // Show a Message
213 Toast t = Toast.makeText(mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG);
214 t.show();
215
216 }
217 }
218
219 public void sendDownloadedFile(OCFile file) {
220 if (file != null) {
221 String storagePath = file.getStoragePath();
222 String encodedStoragePath = WebdavUtils.encodePath(storagePath);
223 Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
224 // set MimeType
225 sendIntent.setType(file.getMimetype());
226 sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + encodedStoragePath));
227 sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action
228
229 // Show dialog, without the own app
230 String[] packagesToExclude = new String[]{mFileActivity.getPackageName()};
231 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent, packagesToExclude, file);
232 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
233
234 } else {
235 Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
236 }
237 }
238
239
240 public void syncFile(OCFile file) {
241
242 if (!file.isFolder()){
243 Intent intent = new Intent(mFileActivity, OperationsService.class);
244 intent.setAction(OperationsService.ACTION_SYNC_FILE);
245 intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
246 intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
247 intent.putExtra(OperationsService.EXTRA_SYNC_FILE_CONTENTS, true);
248 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(intent);
249 mFileActivity.showLoadingDialog();
250
251 } else {
252 Intent intent = new Intent(mFileActivity, OperationsService.class);
253 intent.setAction(OperationsService.ACTION_SYNC_FOLDER);
254 intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
255 intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
256 mFileActivity.startService(intent);
257 }
258 }
259
260 public void toggleFavorite(OCFile file, boolean isFavorite) {
261 file.setFavorite(isFavorite);
262 mFileActivity.getStorageManager().saveFile(file);
263
264 /// register the OCFile instance in the observer service to monitor local updates
265 Intent observedFileIntent = FileObserverService.makeObservedFileIntent(
266 mFileActivity,
267 file,
268 mFileActivity.getAccount(),
269 isFavorite);
270 mFileActivity.startService(observedFileIntent);
271
272 /// immediate content synchronization
273 if (file.isFavorite()) {
274 syncFile(file);
275 }
276 }
277
278 public void renameFile(OCFile file, String newFilename) {
279 // RenameFile
280 Intent service = new Intent(mFileActivity, OperationsService.class);
281 service.setAction(OperationsService.ACTION_RENAME);
282 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
283 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
284 service.putExtra(OperationsService.EXTRA_NEWNAME, newFilename);
285 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
286
287 mFileActivity.showLoadingDialog();
288 }
289
290
291 public void removeFile(OCFile file, boolean onlyLocalCopy) {
292 // RemoveFile
293 Intent service = new Intent(mFileActivity, OperationsService.class);
294 service.setAction(OperationsService.ACTION_REMOVE);
295 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
296 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
297 service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, onlyLocalCopy);
298 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
299
300 mFileActivity.showLoadingDialog();
301 }
302
303
304 public void createFolder(String remotePath, boolean createFullPath) {
305 // Create Folder
306 Intent service = new Intent(mFileActivity, OperationsService.class);
307 service.setAction(OperationsService.ACTION_CREATE_FOLDER);
308 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
309 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath);
310 service.putExtra(OperationsService.EXTRA_CREATE_FULL_PATH, createFullPath);
311 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
312
313 mFileActivity.showLoadingDialog();
314 }
315
316 /**
317 * Cancel the transference in downloads (files/folders) and file uploads
318 * @param file OCFile
319 */
320 public void cancelTransference(OCFile file) {
321 Account account = mFileActivity.getAccount();
322 if (file.isFolder()) {
323 OperationsService.OperationsServiceBinder opsBinder = mFileActivity.getOperationsServiceBinder();
324 if (opsBinder != null) {
325 opsBinder.cancel(account, file);
326 }
327 }
328
329 // for both files and folders
330 FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
331 FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
332 if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
333 downloaderBinder.cancel(account, file);
334
335 // TODO - review why is this here, and solve in a better way
336 // Remove etag for parent, if file is a favorite
337 if (file.isFavorite()) {
338 OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId());
339 parent.setEtag("");
340 mFileActivity.getStorageManager().saveFile(parent);
341 }
342
343 } else if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) {
344 uploaderBinder.cancel(account, file);
345 }
346 }
347
348 /**
349 * Start move file operation
350 *
351 * @param newfile File where it is going to be moved
352 * @param currentFile File with the previous info
353 */
354 public void moveFile(OCFile newfile, OCFile currentFile) {
355 // Move files
356 Intent service = new Intent(mFileActivity, OperationsService.class);
357 service.setAction(OperationsService.ACTION_MOVE_FILE);
358 service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
359 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
360 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
361 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
362
363 mFileActivity.showLoadingDialog();
364 }
365
366 /**
367 * Start copy file operation
368 *
369 * @param newfile File where it is going to be moved
370 * @param currentFile File with the previous info
371 */
372 public void copyFile(OCFile newfile, OCFile currentFile) {
373 // Copy files
374 Intent service = new Intent(mFileActivity, OperationsService.class);
375 service.setAction(OperationsService.ACTION_COPY_FILE);
376 service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
377 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
378 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
379 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
380
381 mFileActivity.showLoadingDialog();
382 }
383
384 public long getOpIdWaitingFor() {
385 return mWaitingForOpId;
386 }
387
388
389 public void setOpIdWaitingFor(long waitingForOpId) {
390 mWaitingForOpId = waitingForOpId;
391 }
392
393 /**
394 * @return 'True' if the server doesn't need to check forbidden characters
395 */
396 public boolean isVersionWithForbiddenCharacters() {
397 if (mFileActivity.getAccount() != null) {
398 OwnCloudVersion serverVersion = AccountUtils.getServerVersion(mFileActivity.getAccount());
399 return (serverVersion != null && serverVersion.isVersionWithForbiddenCharacters());
400 }
401 return false;
402 }
403 }