Merge branch 'setAsWallpaper' into beta
[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 || file.isDown()) {
254 File externalFile=new File(file.getStoragePath());
255 Uri sendUri = Uri.fromFile(externalFile);
256 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
257 intent.setDataAndType(sendUri, file.getMimetype());
258 intent.putExtra("mimeType", file.getMimetype());
259 mFileActivity.startActivityForResult(Intent.createChooser(intent, "Set As"), 200);
260 } else {
261 Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
262 }
263 }
264
265 public void sendCachedImage(OCFile file) {
266 if (file != null) {
267 Intent sendIntent = new Intent(android.content.Intent.ACTION_SEND);
268 // set MimeType
269 sendIntent.setType(file.getMimetype());
270 // sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + "/#" + file.getRemoteId() + "#" + file.getFileName()));
271 sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + DiskLruImageCacheFileProvider.AUTHORITY + file.getRemotePath()));
272 sendIntent.putExtra(Intent.ACTION_SEND, true); // Send Action
273
274 // Show dialog, without the own app
275 String[] packagesToExclude = new String[] { mFileActivity.getPackageName() };
276 DialogFragment chooserDialog = ShareLinkToDialog.newInstance(sendIntent, packagesToExclude, file);
277 chooserDialog.show(mFileActivity.getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
278 } else {
279 Log_OC.wtf(TAG, "Trying to send a NULL OCFile");
280 }
281 }
282
283 public void syncFiles(ArrayList<OCFile> files) {
284 for (OCFile file: files) {
285 syncFile(file);
286 }
287 }
288
289 /**
290 * Request the synchronization of a file or folder with the OC server, including its contents.
291 *
292 * @param file The file or folder to synchronize
293 */
294 public void syncFile(OCFile file) {
295 if (!file.isFolder()){
296 Intent intent = new Intent(mFileActivity, OperationsService.class);
297 intent.setAction(OperationsService.ACTION_SYNC_FILE);
298 intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
299 intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
300 intent.putExtra(OperationsService.EXTRA_SYNC_FILE_CONTENTS, true);
301 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(intent);
302 mFileActivity.showLoadingDialog();
303
304 } else {
305 Intent intent = new Intent(mFileActivity, OperationsService.class);
306 intent.setAction(OperationsService.ACTION_SYNC_FOLDER);
307 intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
308 intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
309 mFileActivity.startService(intent);
310
311 }
312 }
313
314 public void toggleFavorites(ArrayList<OCFile> files, boolean isFavorite){
315 for (OCFile file: files) {
316 toggleFavorite(file, isFavorite);
317 }
318 }
319
320 public void toggleFavorite(OCFile file, boolean isFavorite) {
321 file.setFavorite(isFavorite);
322 mFileActivity.getStorageManager().saveFile(file);
323
324 /// register the OCFile instance in the observer service to monitor local updates
325 Intent observedFileIntent = FileObserverService.makeObservedFileIntent(
326 mFileActivity,
327 file,
328 mFileActivity.getAccount(),
329 isFavorite);
330 mFileActivity.startService(observedFileIntent);
331
332 /// immediate content synchronization
333 if (file.isFavorite()) {
334 syncFile(file);
335 }
336 }
337
338 public void renameFile(OCFile file, String newFilename) {
339 // RenameFile
340 Intent service = new Intent(mFileActivity, OperationsService.class);
341 service.setAction(OperationsService.ACTION_RENAME);
342 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
343 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
344 service.putExtra(OperationsService.EXTRA_NEWNAME, newFilename);
345 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
346
347 mFileActivity.showLoadingDialog();
348 }
349
350
351 public void removeFile(OCFile file, boolean onlyLocalCopy) {
352 // RemoveFile
353 Intent service = new Intent(mFileActivity, OperationsService.class);
354 service.setAction(OperationsService.ACTION_REMOVE);
355 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
356 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath());
357 service.putExtra(OperationsService.EXTRA_REMOVE_ONLY_LOCAL, onlyLocalCopy);
358 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
359
360 mFileActivity.showLoadingDialog();
361 }
362
363
364 public void createFolder(String remotePath, boolean createFullPath) {
365 // Create Folder
366 Intent service = new Intent(mFileActivity, OperationsService.class);
367 service.setAction(OperationsService.ACTION_CREATE_FOLDER);
368 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
369 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, remotePath);
370 service.putExtra(OperationsService.EXTRA_CREATE_FULL_PATH, createFullPath);
371 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
372
373 mFileActivity.showLoadingDialog();
374 }
375
376 /**
377 * Cancel the transference in downloads (files/folders) and file uploads
378 * @param file OCFile
379 */
380 public void cancelTransference(OCFile file) {
381 Account account = mFileActivity.getAccount();
382 if (file.isFolder()) {
383 OperationsService.OperationsServiceBinder opsBinder = mFileActivity.getOperationsServiceBinder();
384 if (opsBinder != null) {
385 opsBinder.cancel(account, file);
386 }
387 }
388
389 // for both files and folders
390 FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder();
391 if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) {
392 downloaderBinder.cancel(account, file);
393 }
394 FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder();
395 if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) {
396 uploaderBinder.cancel(account, file);
397 }
398 }
399
400 /**
401 * Start move file operation
402 *
403 * @param newfile File where it is going to be moved
404 * @param currentFile File with the previous info
405 */
406 public void moveFile(OCFile newfile, OCFile currentFile) {
407 // Move files
408 Intent service = new Intent(mFileActivity, OperationsService.class);
409 service.setAction(OperationsService.ACTION_MOVE_FILE);
410 service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
411 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
412 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
413 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
414
415 // TODO Tobi loading dialog?
416 // mFileActivity.showLoadingDialog();
417 }
418
419 /**
420 * Start copy file operation
421 *
422 * @param newfile File where it is going to be moved
423 * @param currentFile File with the previous info
424 */
425 public void copyFile(OCFile newfile, OCFile currentFile) {
426 // Copy files
427 Intent service = new Intent(mFileActivity, OperationsService.class);
428 service.setAction(OperationsService.ACTION_COPY_FILE);
429 service.putExtra(OperationsService.EXTRA_NEW_PARENT_PATH, newfile.getRemotePath());
430 service.putExtra(OperationsService.EXTRA_REMOTE_PATH, currentFile.getRemotePath());
431 service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount());
432 mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service);
433
434 mFileActivity.showLoadingDialog();
435 }
436
437 public long getOpIdWaitingFor() {
438 return mWaitingForOpId;
439 }
440
441
442 public void setOpIdWaitingFor(long waitingForOpId) {
443 mWaitingForOpId = waitingForOpId;
444 }
445
446 /**
447 * @return 'True' if the server doesn't need to check forbidden characters
448 */
449 public boolean isVersionWithForbiddenCharacters() {
450 if (mFileActivity.getAccount() != null) {
451 OwnCloudVersion serverVersion = AccountUtils.getServerVersion(mFileActivity.getAccount());
452 return (serverVersion != null && serverVersion.isVersionWithForbiddenCharacters());
453 }
454 return false;
455 }
456 }