Merge remote-tracking branch 'remotes/upstream/master' into beta
[pub/Android/ownCloud.git] / src / com / owncloud / android / utils / FileStorageUtils.java
1 /**
2 * ownCloud Android client application
3 *
4 * @author David A. Velasco
5 * Copyright (C) 2015 ownCloud Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2,
9 * as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 package com.owncloud.android.utils;
22
23 import java.io.File;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.io.FileInputStream;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.OutputStream;
31 import java.util.Collections;
32 import java.util.Comparator;
33 import java.util.List;
34 import java.util.Vector;
35
36 import third_parties.daveKoeller.AlphanumComparator;
37
38 import com.owncloud.android.MainApp;
39 import com.owncloud.android.R;
40 import com.owncloud.android.datamodel.OCFile;
41 import com.owncloud.android.lib.resources.files.RemoteFile;
42
43 import android.accounts.Account;
44 import android.annotation.SuppressLint;
45 import android.content.Context;
46 import android.content.SharedPreferences;
47 import android.preference.PreferenceManager;
48 import android.net.Uri;
49 import android.os.StatFs;
50 import android.webkit.MimeTypeMap;
51
52
53 /**
54 * Static methods to help in access to local file system.
55 */
56 public class FileStorageUtils {
57 public static final Integer SORT_NAME = 0;
58 public static final Integer SORT_DATE = 1;
59 public static final Integer SORT_SIZE = 2;
60 public static Integer mSortOrder = SORT_NAME;
61 public static Boolean mSortAscending = true;
62
63
64 public static final String getSavePath(String accountName) {
65 // File sdCard = Environment.getExternalStorageDirectory();
66
67 return MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator + Uri.encode(accountName, "@");
68 // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
69 }
70
71 public static final String getDefaultSavePathFor(String accountName, OCFile file) {
72 return getSavePath(accountName) + file.getRemotePath();
73 }
74
75 public static final String getTemporalPath(String accountName) {
76 return MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator + "tmp" + File.separator + Uri.encode(accountName, "@");
77 // URL encoding is an 'easy fix' to overcome that NTFS and FAT32 don't allow ":" in file names, that can be in the accountName since 0.1.190B
78 }
79
80 @SuppressLint("NewApi")
81 public static final long getUsableSpace(String accountName) {
82 File savePath = new File(MainApp.getStoragePath());
83 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
84 return savePath.getUsableSpace();
85
86 } else {
87 StatFs stats = new StatFs(savePath.getAbsolutePath());
88 return stats.getAvailableBlocks() * stats.getBlockSize();
89 }
90
91 }
92
93 public static final String getLogPath() {
94 return MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() + File.separator + "log";
95 }
96
97 public static String getInstantUploadFilePath(Context context, String fileName) {
98 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
99 String uploadPathdef = context.getString(R.string.instant_upload_path);
100 String uploadPath = pref.getString("instant_upload_path", uploadPathdef);
101 String value = uploadPath + OCFile.PATH_SEPARATOR + (fileName == null ? "" : fileName);
102 return value;
103 }
104
105 /**
106 * Gets the composed path when video is or must be stored
107 * @param context
108 * @param fileName: video file name
109 * @return String: video file path composed
110 */
111 public static String getInstantVideoUploadFilePath(Context context, String fileName) {
112 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
113 String uploadVideoPathdef = context.getString(R.string.instant_upload_path);
114 String uploadVideoPath = pref.getString("instant_video_upload_path", uploadVideoPathdef);
115 String value = uploadVideoPath + OCFile.PATH_SEPARATOR + (fileName == null ? "" : fileName);
116 return value;
117 }
118
119 public static String getParentPath(String remotePath) {
120 String parentPath = new File(remotePath).getParent();
121 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
122 return parentPath;
123 }
124
125 /**
126 * Creates and populates a new {@link OCFile} object with the data read from the server.
127 *
128 * @param remote remote file read from the server (remote file or folder).
129 * @return New OCFile instance representing the remote resource described by remote.
130 */
131 public static OCFile fillOCFile(RemoteFile remote) {
132 OCFile file = new OCFile(remote.getRemotePath());
133 file.setCreationTimestamp(remote.getCreationTimestamp());
134 file.setFileLength(remote.getLength());
135 file.setMimetype(remote.getMimeType());
136 file.setModificationTimestamp(remote.getModifiedTimestamp());
137 file.setEtag(remote.getEtag());
138 file.setPermissions(remote.getPermissions());
139 file.setRemoteId(remote.getRemoteId());
140 return file;
141 }
142
143 /**
144 * Creates and populates a new {@link RemoteFile} object with the data read from an {@link OCFile}.
145 *
146 * @param ocFile OCFile
147 * @return New RemoteFile instance representing the resource described by ocFile.
148 */
149 public static RemoteFile fillRemoteFile(OCFile ocFile){
150 RemoteFile file = new RemoteFile(ocFile.getRemotePath());
151 file.setCreationTimestamp(ocFile.getCreationTimestamp());
152 file.setLength(ocFile.getFileLength());
153 file.setMimeType(ocFile.getMimetype());
154 file.setModifiedTimestamp(ocFile.getModificationTimestamp());
155 file.setEtag(ocFile.getEtag());
156 file.setPermissions(ocFile.getPermissions());
157 file.setRemoteId(ocFile.getRemoteId());
158 return file;
159 }
160
161 /**
162 * Sorts all filenames, regarding last user decision
163 */
164 public static Vector<OCFile> sortOcFolder(Vector<OCFile> files){
165 switch (mSortOrder){
166 case 0:
167 files = FileStorageUtils.sortOCFilesByName(files);
168 break;
169 case 1:
170 files = FileStorageUtils.sortOCFilesByDate(files);
171 break;
172 case 2:
173 // mFiles = FileStorageUtils.sortBySize(mSortAscending);
174 break;
175 }
176
177 return files;
178 }
179
180 /**
181 * Sorts all filenames, regarding last user decision
182 */
183 public static File[] sortLocalFolder(File[] files){
184 switch (mSortOrder){
185 case 0:
186 files = FileStorageUtils.sortLocalFilesByName(files);
187 break;
188 case 1:
189 files = FileStorageUtils.sortLocalFilesByDate(files);
190 break;
191 case 2:
192 // mFiles = FileStorageUtils.sortBySize(mSortAscending);
193 break;
194 }
195
196 return files;
197 }
198
199 /**
200 * Sorts list by Date
201 * @param files
202 */
203 public static Vector<OCFile> sortOCFilesByDate(Vector<OCFile> files){
204 final Integer val;
205 if (mSortAscending){
206 val = 1;
207 } else {
208 val = -1;
209 }
210
211 Collections.sort(files, new Comparator<OCFile>() {
212 public int compare(OCFile o1, OCFile o2) {
213 if (o1.isFolder() && o2.isFolder()) {
214 Long obj1 = o1.getModificationTimestamp();
215 return val * obj1.compareTo(o2.getModificationTimestamp());
216 }
217 else if (o1.isFolder()) {
218 return -1;
219 } else if (o2.isFolder()) {
220 return 1;
221 } else if (o1.getModificationTimestamp() == 0 || o2.getModificationTimestamp() == 0){
222 return 0;
223 } else {
224 Long obj1 = o1.getModificationTimestamp();
225 return val * obj1.compareTo(o2.getModificationTimestamp());
226 }
227 }
228 });
229
230 return files;
231 }
232
233 /**
234 * Sorts list by Date
235 * @param filesArray
236 */
237 public static File[] sortLocalFilesByDate(File[] filesArray){
238 final Integer val;
239 if (mSortAscending){
240 val = 1;
241 } else {
242 val = -1;
243 }
244
245 List<File> files = new ArrayList<File>(Arrays.asList(filesArray));
246
247 Collections.sort(files, new Comparator<File>() {
248 public int compare(File o1, File o2) {
249 if (o1.isDirectory() && o2.isDirectory()) {
250 Long obj1 = o1.lastModified();
251 return val * obj1.compareTo(o2.lastModified());
252 }
253 else if (o1.isDirectory()) {
254 return -1;
255 } else if (o2.isDirectory()) {
256 return 1;
257 } else if (o1.lastModified() == 0 || o2.lastModified() == 0){
258 return 0;
259 } else {
260 Long obj1 = o1.lastModified();
261 return val * obj1.compareTo(o2.lastModified());
262 }
263 }
264 });
265
266 File[] returnArray = new File[1];
267 return files.toArray(returnArray);
268 }
269
270 // /**
271 // * Sorts list by Size
272 // * @param sortAscending true: ascending, false: descending
273 // */
274 // public static Vector<OCFile> sortBySize(Vector<OCFile> files){
275 // final Integer val;
276 // if (mSortAscending){
277 // val = 1;
278 // } else {
279 // val = -1;
280 // }
281 //
282 // Collections.sort(files, new Comparator<OCFile>() {
283 // public int compare(OCFile o1, OCFile o2) {
284 // if (o1.isFolder() && o2.isFolder()) {
285 // Long obj1 = getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o1)));
286 // return val * obj1.compareTo(getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o2))));
287 // }
288 // else if (o1.isFolder()) {
289 // return -1;
290 // } else if (o2.isFolder()) {
291 // return 1;
292 // } else if (o1.getFileLength() == 0 || o2.getFileLength() == 0){
293 // return 0;
294 // } else {
295 // Long obj1 = o1.getFileLength();
296 // return val * obj1.compareTo(o2.getFileLength());
297 // }
298 // }
299 // });
300 //
301 // return files;
302 // }
303
304 /**
305 * Sorts list by Name
306 * @param files files to sort
307 */
308 public static Vector<OCFile> sortOCFilesByName(Vector<OCFile> files){
309 final Integer val;
310 if (mSortAscending){
311 val = 1;
312 } else {
313 val = -1;
314 }
315
316 Collections.sort(files, new Comparator<OCFile>() {
317 public int compare(OCFile o1, OCFile o2) {
318 if (o1.isFolder() && o2.isFolder()) {
319 return val * new AlphanumComparator().compare(o1, o2);
320 } else if (o1.isFolder()) {
321 return -1;
322 } else if (o2.isFolder()) {
323 return 1;
324 }
325 return val * new AlphanumComparator().compare(o1, o2);
326 }
327 });
328
329 return files;
330 }
331
332 /**
333 * Sorts list by Name
334 * @param filesArray files to sort
335 */
336 public static File[] sortLocalFilesByName(File[] filesArray){
337 final Integer val;
338 if (mSortAscending){
339 val = 1;
340 } else {
341 val = -1;
342 }
343
344 List<File> files = new ArrayList<File>(Arrays.asList(filesArray));
345
346 Collections.sort(files, new Comparator<File>() {
347 public int compare(File o1, File o2) {
348 if (o1.isDirectory() && o2.isDirectory()) {
349 return val * o1.getPath().toLowerCase().compareTo(o2.getPath().toLowerCase());
350 } else if (o1.isDirectory()) {
351 return -1;
352 } else if (o2.isDirectory()) {
353 return 1;
354 }
355 return val * new AlphanumComparator().compare(o1.getPath().toLowerCase(),
356 o2.getPath().toLowerCase());
357 }
358 });
359
360 File[] returnArray = new File[1];
361 return files.toArray(returnArray);
362 }
363
364 /**
365 * Local Folder size
366 * @param dir File
367 * @return Size in bytes
368 */
369 public static long getFolderSize(File dir) {
370 if (dir.exists()) {
371 long result = 0;
372 for (File f : dir.listFiles()) {
373 if (f.isDirectory())
374 result += getFolderSize(f);
375 else
376 result += f.length();
377 }
378 return result;
379 }
380 return 0;
381 }
382
383 /**
384 * Mimetype String of a file
385 * @param path
386 * @return
387 */
388 public static String getMimeTypeFromName(String path) {
389 String extension = "";
390 int pos = path.lastIndexOf('.');
391 if (pos >= 0) {
392 extension = path.substring(pos + 1);
393 }
394 String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
395 return (result != null) ? result : "";
396 }
397
398 /**
399 * Scans the default location for saving local copies of files searching for
400 * a 'lost' file with the same full name as the {@link OCFile} received as
401 * parameter.
402 *
403 * This method helps to keep linked local copies of the files when the app is uninstalled, and then
404 * reinstalled in the device. OR after the cache of the app was deleted in system settings.
405 *
406 * The method is assuming that all the local changes in the file where synchronized in the past. This is dangerous,
407 * but assuming the contrary could lead to massive unnecessary synchronizations of downloaded file after deleting
408 * the app cache.
409 *
410 * This should be changed in the near future to avoid any chance of data loss, but we need to add some options
411 * to limit hard automatic synchronizations to wifi, unless the user wants otherwise.
412 *
413 * @param file File to associate a possible 'lost' local file.
414 * @param account Account holding file.
415 */
416 public static void searchForLocalFileInDefaultPath(OCFile file, Account account) {
417 if (file.getStoragePath() == null && !file.isFolder()) {
418 File f = new File(FileStorageUtils.getDefaultSavePathFor(account.name, file));
419 if (f.exists()) {
420 file.setStoragePath(f.getAbsolutePath());
421 file.setLastSyncDateForData(f.lastModified());
422 }
423 }
424 }
425
426 public static boolean copyFile(File src, File target) {
427 boolean ret = true;
428
429 InputStream in = null;
430 OutputStream out = null;
431
432 try {
433 in = new FileInputStream(src);
434 out = new FileOutputStream(target);
435 byte[] buf = new byte[1024];
436 int len;
437 while ((len = in.read(buf)) > 0) {
438 out.write(buf, 0, len);
439 }
440 } catch (IOException ex) {
441 ret = false;
442 } finally {
443 if (in != null) try {
444 in.close();
445 } catch (IOException e) {
446 e.printStackTrace(System.err);
447 }
448 if (out != null) try {
449 out.close();
450 } catch (IOException e) {
451 e.printStackTrace(System.err);
452 }
453 }
454
455 return ret;
456 }
457
458 }