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