wip
[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.graphics.Bitmap;
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.status.OwnCloudVersion;
46 import com.owncloud.android.services.OperationsService;
47 import com.owncloud.android.services.observer.FileObserverService;
48 import com.owncloud.android.ui.activity.FileActivity;
49 import com.owncloud.android.ui.adapter.DiskLruImageCacheFileProvider;
50 import com.owncloud.android.ui.dialog.ShareLinkToDialog;
51
52 import org.apache.http.protocol.HTTP;
53
54 import java.io.File;
55 import java.util.List;
56
57 import java.io.ByteArrayOutputStream;
58 import java.io.File;
59 import java.io.FileNotFoundException;
60 import java.io.FileOutputStream;
61 import java.io.IOException;
62
63 import java.util.ArrayList;
64
65 /**
66 *
67 */
68 public class FileOperationsHelper {
69
70 private static final String TAG = FileOperationsHelper.class.getName();
71
72 private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
73
74 protected FileActivity mFileActivity = null;
75
76 /// Identifier of operation in progress which result shouldn't be lost
77 private long mWaitingForOpId = Long.MAX_VALUE;
78
79 public FileOperationsHelper(FileActivity fileActivity) {
80 mFileActivity = fileActivity;
81 }
82
83
84 public void openFile(OCFile file) {
85 if (file != null) {
86 String storagePath = file.getStoragePath();
87 String encodedStoragePath = WebdavUtils.encodePath(storagePath);
88
89 Intent intentForSavedMimeType = new Intent(Intent.ACTION_VIEW);
90 intentForSavedMimeType.setDataAndType(Uri.parse("file://"+ encodedStoragePath), 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.setDataAndType(Uri.parse("file://"+ encodedStoragePath), guessedMimeType);
103 intentForGuessedMimeType.setFlags(
104 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
105 );
106 }
107 }
108
109 Intent openFileWithIntent;
110 if (intentForGuessedMimeType != null) {
111 openFileWithIntent = intentForGuessedMimeType;
112 } else {
113 openFileWithIntent = intentForSavedMimeType;
114 }
115
116 List<ResolveInfo> launchables = mFileActivity.getPackageManager().
117 queryIntentActivities(openFileWithIntent, PackageManager.GET_INTENT_FILTERS);
118
119 if(launchables != null && launchables.size() > 0) {
120 try {
121 mFileActivity.startActivity(
122 Intent.createChooser(
123 openFileWithIntent, mFileActivity.getString(R.string.actionbar_open_with)
124 )
125 );
126 } catch (ActivityNotFoundException anfe) {
127 showNoAppForFileTypeToast(mFileActivity.getApplicationContext());
128 }
129 } else {
130 showNoAppForFileTypeToast(mFileActivity.getApplicationContext());
131 }
132
133 } else {
134 Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
135 }
136 }
137
138 /**
139 * Displays a toast stating that no application could be found to open the file.
140 *
141 * @param context the context to be able to show a toast.
142 */
143 private void showNoAppForFileTypeToast(Context context) {
144 Toast.makeText(context,
145 R.string.file_list_no_app_for_file_type, Toast.LENGTH_SHORT)
146 .show();
147 }
148
149 public void shareFileWithLink(OCFile file) {
150
151 if (isSharedSupported()) {
152 if (file != null) {
153 String link = "https://fake.url";
154 Intent intent = createShareWithLinkIntent(link);
155 String[] packagesToExclude = new String[]{mFileActivity.getPackageName()};
156 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intent, packagesToExclude, file);
157 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
158
159 } else {
160 Log_OC.wtf(TAG, "Trying to share a NULL OCFile");
161 }
162
163 } else {
164 // Show a Message
165 Toast t = Toast.makeText(
166 mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG
167 );
168 t.show();
169 }
170 }
171
172
173 public void shareFileWithLinkToApp(OCFile file, String password, Intent sendIntent) {
174
175 if (file != null) {
176 mFileActivity.showLoadingDialog();
177
178 Intent service = new Intent(mFileActivity, OperationsService.class);
179 service.setAction(OperationsService.ACTION_CREATE_SHARE);
180 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
181 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
182 service.putExtra(OperationsService.EXTRA_PASSWORD_SHARE, password);
183 service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent);
184 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
185
186 } else {
187 Log_OC.wtf(TAG, "Trying to open a NULL OCFile");
188 }
189 }
190
191
192 private Intent createShareWithLinkIntent(String link) {
193 Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
194 intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
195 intentToShareLink.setType(HTTP.PLAIN_TEXT_TYPE);
196 return intentToShareLink;
197 }
198
199
200 /**
201 * @return 'True' if the server supports the Share API
202 */
203 public boolean isSharedSupported() {
204 if (mFileActivity.getAccount() != null) {
205 OwnCloudVersion serverVersion = AccountUtils.getServerVersion(mFileActivity.getAccount());
206 return (serverVersion != null && serverVersion.isSharedSupported());
207 }
208 return false;
209 }
210
211
212 public void unshareFileWithLink(OCFile file) {
213
214 if (isSharedSupported()) {
215 // Unshare the file
216 Intent service = new Intent(mFileActivity, OperationsService.class);
217 service.setAction(OperationsService.ACTION_UNSHARE);
218 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
219 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
220 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
221
222 mFileActivity.showLoadingDialog();
223
224 } else {
225 // Show a Message
226 Toast t = Toast.makeText(mFileActivity, mFileActivity.getString(R.string.share_link_no_support_share_api), Toast.LENGTH_LONG);
227 t.show();
228
229 }
230 }
231
232 public void sendDownloadedFile(OCFile file) {
233 if (file != null) {
234 String storagePath = file.getStoragePath();
235 String encodedStoragePath = WebdavUtils.encodePath(storagePath);
236 Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
237 // set MimeType
238 sendIntent.setType(file.getMimetype());
239 sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + encodedStoragePath));
240 sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action
241
242 // Show dialog, without the own app
243 String[] packagesToExclude = new String[]{mFileActivity.getPackageName()};
244 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent, packagesToExclude, file);
245 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
246
247 } else {
248 Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
249 }
250 }
251
252 public void setPictureAs(OCFile file) {
253 if (file != null){
254 if (file.isDown()) {
255 File externalFile = new File(file.getStoragePath());
256 Uri sendUri = Uri.fromFile(externalFile);
257 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
258 intent.setDataAndType(sendUri, file.getMimetype());
259 intent.putExtra("mimeType", file.getMimetype());
260 mFileActivity.startActivityForResult(Intent.createChooser(intent, "Set As"), 200);
261 } else {
262 // TODO re-enable after resized images is available
263 Uri sendUri = Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + file.getRemotePath());
264 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
265 intent.setDataAndType(sendUri, file.getMimetype());
266 intent.putExtra("mimeType", file.getMimetype());
267 mFileActivity.startActivityForResult(Intent.createChooser(intent, "Set As"), 200);
268
269 // Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
270 // // set MimeType
271 // sendIntent.setType(file.getMimetype());
272 //// sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + "/#" + file.getRemoteId() + "#" + file.getFileName()));
273 // sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + file.getRemotePath()));
274 // sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action
275 //
276 // // Show dialog, without the own app
277 // String[] packagesToExclude = new String[] { mFileActivity.getPackageName() };
278 // DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent, packagesToExclude, file);
279 // chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
280 }
281 } else {
282 Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
283 }
284 }
285
286 public void sendCachedImage(OCFile file) {
287 if (file != null) {
288 Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
289 // set MimeType
290 sendIntent.setType(file.getMimetype());
291 // sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + "/#" + file.getRemoteId() + "#" + file.getFileName()));
292 sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + file.getRemotePath()));
293 sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action
294
295 // Show dialog, without the own app
296 String[] packagesToExclude = new String[] { mFileActivity.getPackageName() };
297 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent, packagesToExclude, file);
298 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
299 } else {
300 Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
301 }
302 }
303
304 public void syncFiles(ArrayList<OCFile> files) {
305 for (OCFile file: files) {
306 syncFile(file);
307 }
308 }
309
310 /**
311 * Request the synchronization of a file or folder with the OC server, including its contents.
312 *
313 * @param file The file or folder to synchronize
314 */
315 public void syncFile(OCFile file) {
316 if (!file.isFolder()){
317 Intent intent = new Intent(mFileActivity, OperationsService.class);
318 intent.setAction(OperationsService.ACTION_SYNC_FILE);
319 intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
320 intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
321 intent.putExtra(OperationsService.EXTRA_SYNC_FILE_CONTENTS, true);
322 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(intent);
323 mFileActivity.showLoadingDialog();
324
325 } else {
326 Intent intent = new Intent(mFileActivity, OperationsService.class);
327 intent.setAction(OperationsService.ACTION_SYNC_FOLDER);
328 intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
329 intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
330 mFileActivity.startService(intent);
331
332 }
333 }
334
335 public void toggleFavorites(ArrayList<OCFile> files, boolean isFavorite){
336 for (OCFile file: files) {
337 toggleFavorite(file, isFavorite);
338 }
339 }
340
341 public void toggleFavorite(OCFile file, boolean isFavorite) {
342 file.setFavorite(isFavorite);
343 mFileActivity.getStorageManager().saveFile(file);
344
345 /// register the OCFile instance in the observer service to monitor local updates
346 Intent observedFileIntent = FileObserverService.makeObservedFileIntent(
347 mFileActivity,
348 file,
349 mFileActivity.getAccount(),
350 isFavorite);
351 mFileActivity.startService(observedFileIntent);
352
353 /// immediate content synchronization
354 if (file.isFavorite()) {
355 syncFile(file);
356 }
357 }
358
359 public void renameFile(OCFile file, String newFilename) {
360 // RenameFile
361 Intent service = new Intent(mFileActivity, OperationsService.class);
362 service.setAction(OperationsService.ACTION_RENAME);
363 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
364 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
365 service.putExtra(OperationsService.EXTRA_NEWNAME, newFilename);
366 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
367
368 mFileActivity.showLoadingDialog();
369 }
370
371
372 public void removeFile(OCFile file, boolean onlyLocalCopy) {
373 // RemoveFile
374 Intent service = new Intent(mFileActivity, OperationsService.class);
375 service.setAction(OperationsService.ACTION_REMOVE);
376 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
377 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
378 service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, onlyLocalCopy);
379 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
380
381 mFileActivity.showLoadingDialog();
382 }
383
384
385 public void createFolder(String remotePath, boolean createFullPath) {
386 // Create Folder
387 Intent service = new Intent(mFileActivity, OperationsService.class);
388 service.setAction(OperationsService.ACTION_CREATE_FOLDER);
389 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
390 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath);
391 service.putExtra(OperationsService.EXTRA_CREATE_FULL_PATH, createFullPath);
392 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
393
394 mFileActivity.showLoadingDialog();
395 }
396
397 /**
398 * Cancel the transference in downloads (files/folders) and file uploads
399 * @param file OCFile
400 */
401 public void cancelTransference(OCFile file) {
402 Account account = mFileActivity.getAccount();
403 if (file.isFolder()) {
404 OperationsService.OperationsServiceBinder opsBinder = mFileActivity.getOperationsServiceBinder();
405 if (opsBinder != null) {
406 opsBinder.cancel(account, file);
407 }
408 }
409
410 // for both files and folders
411 FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
412 if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
413 downloaderBinder.cancel(account, file);
414 }
415 FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
416 if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) {
417 uploaderBinder.cancel(account, file);
418 }
419 }
420
421 /**
422 * Start move file operation
423 *
424 * @param newfile File where it is going to be moved
425 * @param currentFile File with the previous info
426 */
427 public void moveFile(OCFile newfile, OCFile currentFile) {
428 // Move files
429 Intent service = new Intent(mFileActivity, OperationsService.class);
430 service.setAction(OperationsService.ACTION_MOVE_FILE);
431 service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
432 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
433 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
434 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
435
436 // TODO Tobi loading dialog?
437 // mFileActivity.showLoadingDialog();
438 }
439
440 /**
441 * Start copy file operation
442 *
443 * @param newfile File where it is going to be moved
444 * @param currentFile File with the previous info
445 */
446 public void copyFile(OCFile newfile, OCFile currentFile) {
447 // Copy files
448 Intent service = new Intent(mFileActivity, OperationsService.class);
449 service.setAction(OperationsService.ACTION_COPY_FILE);
450 service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
451 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
452 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
453 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
454
455 mFileActivity.showLoadingDialog();
456 }
457
458 public long getOpIdWaitingFor() {
459 return mWaitingForOpId;
460 }
461
462
463 public void setOpIdWaitingFor(long waitingForOpId) {
464 mWaitingForOpId = waitingForOpId;
465 }
466
467 /**
468 * @return 'True' if the server doesn't need to check forbidden characters
469 */
470 public boolean isVersionWithForbiddenCharacters() {
471 if (mFileActivity.getAccount() != null) {
472 OwnCloudVersion serverVersion = AccountUtils.getServerVersion(mFileActivity.getAccount());
473 return (serverVersion != null && serverVersion.isVersionWithForbiddenCharacters());
474 }
475 return false;
476 }
477 }