f0f66a7e50c8b02d3b4e24fbd68ad77309c19c33
[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.shares.ShareType;
43 import com.owncloud.android.lib.resources.status.OwnCloudVersion;
44 import com.owncloud.android.services.OperationsService;
45 import com.owncloud.android.services.observer.FileObserverService;
46 import com.owncloud.android.ui.activity.FileActivity;
47 import com.owncloud.android.ui.activity.ShareActivity;
48 import com.owncloud.android.ui.dialog.ShareLinkToDialog;
49 import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
50
51 import org.apache.http.protocol.HTTP;
52
53 import java.util.List;
54
55 /**
56 *
57 */
58 public class FileOperationsHelper {
59
60 private static final String TAG = FileOperationsHelper.class.getName();
61
62 private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
63
64 protected FileActivity mFileActivity = null;
65
66 /// Identifier of operation in progress which result shouldn't be lost
67 private long mWaitingForOpId = Long.MAX_VALUE;
68
69 public FileOperationsHelper(FileActivity fileActivity) {
70 mFileActivity = fileActivity;
71 }
72
73
74 public void openFile(OCFile file) {
75 if (file != null) {
76 String storagePath = file.getStoragePath();
77 String encodedStoragePath = WebdavUtils.encodePath(storagePath);
78
79 Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
80 intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath),
81 file.getMimetype());
82 intentForSavedMimeType.setFlags(
83 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
84 );
85
86 Intent intentForGuessedMimeType = null;
87 if (storagePath.lastIndexOf('.') >= 0) {
88 String guessedMimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
89 storagePath.substring(storagePath.lastIndexOf('.') + 1)
90 );
91 if (guessedMimeType != null && !guessedMimeType.equals(file.getMimetype())) {
92 intentForGuessedMimeType = new Intent(Intent.ACTION_VIEW);
93 intentForGuessedMimeType.
94 setDataAndType(Uri.parse("file://"+ encodedStoragePath),
95 guessedMimeType);
96 intentForGuessedMimeType.setFlags(
97 Intent.FLAG_GRANT_READ_URI_PERMISSION |
98 Intent.FLAG_GRANT_WRITE_URI_PERMISSION
99 );
100 }
101 }
102
103 Intent openFileWithIntent;
104 if (intentForGuessedMimeType != null) {
105 openFileWithIntent = intentForGuessedMimeType;
106 } else {
107 openFileWithIntent = intentForSavedMimeType;
108 }
109
110 List<ResolveInfo> launchables = mFileActivity.getPackageManager().
111 queryIntentActivities(openFileWithIntent, PackageManager.GET_INTENT_FILTERS);
112
113 if(launchables != null && launchables.size() > 0) {
114 try {
115 mFileActivity.startActivity(
116 Intent.createChooser(
117 openFileWithIntent, mFileActivity.getString(R.string.actionbar_open_with)
118 )
119 );
120 } catch (ActivityNotFoundException anfe) {
121 showNoAppForFileTypeToast(mFileActivity.getApplicationContext());
122 }
123 } else {
124 showNoAppForFileTypeToast(mFileActivity.getApplicationContext());
125 }
126
127 } else {
128 Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
129 }
130 }
131
132 /**
133 * Displays a toast stating that no application could be found to open the file.
134 *
135 * @param context the context to be able to show a toast.
136 */
137 private void showNoAppForFileTypeToast(Context context) {
138 Toast.makeText(context,
139 R.string.file_list_no_app_for_file_type, Toast.LENGTH_SHORT)
140 .show();
141 }
142
143
144 /**
145 * Helper method to share a file via a public link. Starts a request to do it in {@link OperationsService}
146 *
147 * @param file The file to share.
148 */
149 public void shareFileViaLink(OCFile file) {
150 if (isSharedSupported()) {
151 if (file != null) {
152 mFileActivity.showLoadingDialog(
153 mFileActivity.getApplicationContext().
154 getString(R.string.wait_a_moment)
155 );
156 Intent service = new Intent(mFileActivity, OperationsService.class);
157 service.setAction(OperationsService.ACTION_CREATE_SHARE_VIA_LINK);
158 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
159 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
160 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
161
162 } else {
163 Log_OC.wtf(TAG, "Trying to share a NULL OCFile");
164 // TODO user-level error?
165 }
166
167 } else {
168 // Show a Message
169 Toast t = Toast.makeText(
170 mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api),
171 Toast.LENGTH_LONG
172 );
173 t.show();
174 }
175 }
176
177
178 public void shareFileWithLinkOLD(OCFile file) {
179
180 if (isSharedSupported()) {
181 if (file != null) {
182 String link = "https://fake.url";
183 Intent intent = createShareWithLinkIntent(link);
184 String[] packagesToExclude = new String[]{mFileActivity.getPackageName()};
185 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intent,
186 packagesToExclude, file);
187 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
188
189 } else {
190 Log_OC.wtf(TAG, "Trying to share a NULL OCFile");
191 }
192
193 } else {
194 // Show a Message
195 Toast t = Toast.makeText(
196 mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api),
197 Toast.LENGTH_LONG
198 );
199 t.show();
200 }
201 }
202
203
204 public void shareFileWithLinkToApp(OCFile file, String password, Intent sendIntent) {
205
206 if (file != null) {
207 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
208 getString(R.string.wait_a_moment));
209
210 Intent service = new Intent(mFileActivity, OperationsService.class);
211 service.setAction(OperationsService.ACTION_CREATE_SHARE_VIA_LINK);
212 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
213 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
214 service.putExtra(OperationsService.EXTRA_PASSWORD_SHARE, password);
215 service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent);
216 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
217
218 } else {
219 Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
220 }
221 }
222
223
224 private Intent createShareWithLinkIntent(String link) {
225 Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
226 intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
227 intentToShareLink.setType(HTTP.PLAIN_TEXT_TYPE);
228 return intentToShareLink;
229 }
230
231
232 /**
233 * Helper method to share a file with a known sharee. Starts a request to do it in {@link OperationsService}
234 *
235 * @param file The file to share.
236 * @param shareeName Name (user name or group name) of the target sharee.
237 * @param shareType The share type determines the sharee type.
238 */
239 public void shareFileWithSharee(OCFile file, String shareeName, ShareType shareType) {
240 if (file != null) {
241 // TODO check capability?
242 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
243 getString(R.string.wait_a_moment));
244
245 Intent service = new Intent(mFileActivity, OperationsService.class);
246 service.setAction(OperationsService.ACTION_CREATE_SHARE_WITH_SHAREE);
247 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
248 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
249 service.putExtra(OperationsService.EXTRA_SHARE_WITH, shareeName);
250 service.putExtra(OperationsService.EXTRA_SHARE_TYPE, shareType);
251 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
252
253 } else {
254 Log_OC.wtf(TAG, "Trying to share a NULL OCFile");
255 }
256 }
257
258
259 /**
260 * @return 'True' if the server supports the Share API
261 */
262 public boolean isSharedSupported() {
263 if (mFileActivity.getAccount() != null) {
264 OwnCloudVersion serverVersion = AccountUtils.getServerVersion(mFileActivity.getAccount());
265 return (serverVersion != null && serverVersion.isSharedSupported());
266 }
267 return false;
268 }
269
270
271 /**
272 * Helper method to unshare a file publicly shared via link.
273 * Starts a request to do it in {@link OperationsService}
274 *
275 * @param file The file to unshare.
276 */
277 public void unshareFileViaLink(OCFile file) {
278
279 // Unshare the file: Create the intent
280 Intent unshareService = new Intent(mFileActivity, OperationsService.class);
281 unshareService.setAction(OperationsService.ACTION_UNSHARE);
282 unshareService.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
283 unshareService.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
284 unshareService.putExtra(OperationsService.EXTRA_SHARE_TYPE, ShareType.PUBLIC_LINK);
285 unshareService.putExtra(OperationsService.EXTRA_SHARE_WITH, "");
286
287 queueShareIntent(unshareService);
288 }
289
290 public void unshareFileWithUserOrGroup(OCFile file, ShareType shareType, String userOrGroup){
291
292 // Unshare the file: Create the intent
293 Intent unshareService = new Intent(mFileActivity, OperationsService.class);
294 unshareService.setAction(OperationsService.ACTION_UNSHARE);
295 unshareService.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
296 unshareService.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
297 unshareService.putExtra(OperationsService.EXTRA_SHARE_TYPE, shareType);
298 unshareService.putExtra(OperationsService.EXTRA_SHARE_WITH, userOrGroup);
299
300 queueShareIntent(unshareService);
301 }
302
303
304 private void queueShareIntent(Intent shareIntent){
305 if (isSharedSupported()) {
306 // Unshare the file
307 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().
308 queueNewOperation(shareIntent);
309
310 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
311 getString(R.string.wait_a_moment));
312
313 } else {
314 // Show a Message
315 Toast t = Toast.makeText(mFileActivity,
316 mFileActivity.getString(R.string.share_link_no_support_share_api),
317 Toast.LENGTH_LONG);
318 t.show();
319
320 }
321 }
322
323 /**
324 * Show an instance of {@link ShareType} for sharing or unsharing the {@OCFile} received as parameter.
325 *
326 * @param file File to share or unshare.
327 */
328 public void showShareFile(OCFile file){
329 Intent intent = new Intent(mFileActivity, ShareActivity.class);
330 intent.putExtra(mFileActivity.EXTRA_FILE, file);
331 intent.putExtra(mFileActivity.EXTRA_ACCOUNT, mFileActivity.getAccount());
332 mFileActivity.startActivity(intent);
333
334 }
335
336
337 /**
338 * Starts a dialog that requests a password to the user to protect a share link.
339 *
340 * @param file File which public share will be protected by the requested password
341 */
342 public void requestPasswordForShareViaLink(OCFile file) {
343 SharePasswordDialogFragment dialog =
344 SharePasswordDialogFragment.newInstance(
345 file,
346 null
347 );
348 dialog.show(
349 mFileActivity.getSupportFragmentManager(),
350 SharePasswordDialogFragment.PASSWORD_FRAGMENT
351 );
352 }
353
354 /**
355 * Updates a public share on a file to set its password.
356 * Starts a request to do it in {@link OperationsService}
357 *
358 * @param file File which public share will be protected with a password.
359 * @param password Password to set for the public link; null or empty string to clear
360 * the current password
361 */
362 public void setPasswordToShareViaLink(OCFile file, String password) {
363 // Set password updating share
364 Intent updateShareIntent = new Intent(mFileActivity, OperationsService.class);
365 updateShareIntent.setAction(OperationsService.ACTION_UPDATE_SHARE);
366 updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
367 updateShareIntent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
368 updateShareIntent.putExtra(
369 OperationsService.EXTRA_PASSWORD_SHARE,
370 (password == null) ? "" : password
371 );
372
373 queueShareIntent(updateShareIntent);
374 }
375
376
377 /**
378 * @return 'True' if the server supports the Search Users API
379 */
380 public boolean isSearchUsersSupportedSupported() {
381 if (mFileActivity.getAccount() != null) {
382 OwnCloudVersion serverVersion = AccountUtils.getServerVersion(mFileActivity.getAccount());
383 return (serverVersion != null && serverVersion.isSearchUsersSupported());
384 }
385 return false;
386 }
387
388 public void sendDownloadedFile(OCFile file) {
389 if (file != null) {
390 String storagePath = file.getStoragePath();
391 String encodedStoragePath = WebdavUtils.encodePath(storagePath);
392 Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
393 // set MimeType
394 sendIntent.setType(file.getMimetype());
395 sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + encodedStoragePath));
396 sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action
397
398 // Show dialog, without the own app
399 String[] packagesToExclude = new String[]{mFileActivity.getPackageName()};
400 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent,
401 packagesToExclude, file);
402 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
403
404 } else {
405 Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
406 }
407 }
408
409 /**
410 * Request the synchronization of a file or folder with the OC server, including its contents.
411 *
412 * @param file The file or folder to synchronize
413 */
414 public void syncFile(OCFile file) {
415 if (!file.isFolder()){
416 Intent intent = new Intent(mFileActivity, OperationsService.class);
417 intent.setAction(OperationsService.ACTION_SYNC_FILE);
418 intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
419 intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
420 intent.putExtra(OperationsService.EXTRA_SYNC_FILE_CONTENTS, true);
421 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(intent);
422 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
423 getString(R.string.wait_a_moment));
424
425 } else {
426 Intent intent = new Intent(mFileActivity, OperationsService.class);
427 intent.setAction(OperationsService.ACTION_SYNC_FOLDER);
428 intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
429 intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
430 mFileActivity.startService(intent);
431
432 }
433 }
434
435 public void toggleFavorite(OCFile file, boolean isFavorite) {
436 file.setFavorite(isFavorite);
437 mFileActivity.getStorageManager().saveFile(file);
438
439 /// register the OCFile instance in the observer service to monitor local updates
440 Intent observedFileIntent = FileObserverService.makeObservedFileIntent(
441 mFileActivity,
442 file,
443 mFileActivity.getAccount(),
444 isFavorite);
445 mFileActivity.startService(observedFileIntent);
446
447 /// immediate content synchronization
448 if (file.isFavorite()) {
449 syncFile(file);
450 }
451 }
452
453 public void renameFile(OCFile file, String newFilename) {
454 // RenameFile
455 Intent service = new Intent(mFileActivity, OperationsService.class);
456 service.setAction(OperationsService.ACTION_RENAME);
457 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
458 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
459 service.putExtra(OperationsService.EXTRA_NEWNAME, newFilename);
460 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
461
462 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
463 getString(R.string.wait_a_moment));
464 }
465
466
467 public void removeFile(OCFile file, boolean onlyLocalCopy) {
468 // RemoveFile
469 Intent service = new Intent(mFileActivity, OperationsService.class);
470 service.setAction(OperationsService.ACTION_REMOVE);
471 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
472 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
473 service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, onlyLocalCopy);
474 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
475
476 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
477 getString(R.string.wait_a_moment));
478 }
479
480
481 public void createFolder(String remotePath, boolean createFullPath) {
482 // Create Folder
483 Intent service = new Intent(mFileActivity, OperationsService.class);
484 service.setAction(OperationsService.ACTION_CREATE_FOLDER);
485 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
486 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath);
487 service.putExtra(OperationsService.EXTRA_CREATE_FULL_PATH, createFullPath);
488 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
489
490 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
491 getString(R.string.wait_a_moment));
492 }
493
494 /**
495 * Cancel the transference in downloads (files/folders) and file uploads
496 * @param file OCFile
497 */
498 public void cancelTransference(OCFile file) {
499 Account account = mFileActivity.getAccount();
500 if (file.isFolder()) {
501 OperationsService.OperationsServiceBinder opsBinder =
502 mFileActivity.getOperationsServiceBinder();
503 if (opsBinder != null) {
504 opsBinder.cancel(account, file);
505 }
506 }
507
508 // for both files and folders
509 FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
510 if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
511 downloaderBinder.cancel(account, file);
512 }
513 FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
514 if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) {
515 uploaderBinder.cancel(account, file);
516 }
517 }
518
519 /**
520 * Start move file operation
521 *
522 * @param newfile File where it is going to be moved
523 * @param currentFile File with the previous info
524 */
525 public void moveFile(OCFile newfile, OCFile currentFile) {
526 // Move files
527 Intent service = new Intent(mFileActivity, OperationsService.class);
528 service.setAction(OperationsService.ACTION_MOVE_FILE);
529 service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
530 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
531 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
532 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
533
534 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
535 getString(R.string.wait_a_moment));
536 }
537
538 /**
539 * Start copy file operation
540 *
541 * @param newfile File where it is going to be moved
542 * @param currentFile File with the previous info
543 */
544 public void copyFile(OCFile newfile, OCFile currentFile) {
545 // Copy files
546 Intent service = new Intent(mFileActivity, OperationsService.class);
547 service.setAction(OperationsService.ACTION_COPY_FILE);
548 service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
549 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
550 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
551 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
552
553 mFileActivity.showLoadingDialog(mFileActivity.getApplicationContext().
554 getString(R.string.wait_a_moment));
555 }
556
557 public long getOpIdWaitingFor() {
558 return mWaitingForOpId;
559 }
560
561
562 public void setOpIdWaitingFor(long waitingForOpId) {
563 mWaitingForOpId = waitingForOpId;
564 }
565
566 /**
567 * @return 'True' if the server doesn't need to check forbidden characters
568 */
569 public boolean isVersionWithForbiddenCharacters() {
570 if (mFileActivity.getAccount() != null) {
571 OwnCloudVersion serverVersion =
572 AccountUtils.getServerVersion(mFileActivity.getAccount());
573 return (serverVersion != null && serverVersion.isVersionWithForbiddenCharacters());
574 }
575 return false;
576 }
577
578 }