Merge tag 'oc-android-1.9' into sdcard-save
[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.Collections;
25 import java.util.Comparator;
26 import java.util.Vector;
27
28 import third_parties.daveKoeller.AlphanumComparator;
29
30 import com.owncloud.android.MainApp;
31 import com.owncloud.android.R;
32 import com.owncloud.android.datamodel.OCFile;
33 import com.owncloud.android.lib.resources.files.RemoteFile;
34
35 import android.accounts.Account;
36 import android.annotation.SuppressLint;
37 import android.content.Context;
38 import android.content.SharedPreferences;
39 import android.preference.PreferenceManager;
40 import android.net.Uri;
41 import android.os.Environment;
42 import android.os.StatFs;
43 import android.webkit.MimeTypeMap;
44
45
46 /**
47 * Static methods to help in access to local file system.
48 */
49 public class FileStorageUtils {
50 public static final Integer SORT_NAME = 0;
51 public static final Integer SORT_DATE = 1;
52 public static final Integer SORT_SIZE = 2;
53 public static Integer mSortOrder = SORT_NAME;
54 public static Boolean mSortAscending = true;
55
56 //private static final String TAG = FileStorageUtils.class.getSimpleName();
57
58 @SuppressLint("NewApi")
59 private static final File getBaseStorePath() {
60 File baseStoragePath = Environment.getExternalStorageDirectory();
61 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
62 File[] dirs = MainApp.getAppContext().getExternalFilesDirs(null);
63 if (dirs.length > 1) {
64 baseStoragePath = dirs[1];
65 }
66 }
67 return baseStoragePath;
68 }
69
70 @SuppressLint("NewApi")
71 private static final String getBaseStorePathString() {
72 File baseStoragePath = Environment.getExternalStorageDirectory();
73 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
74 File[] dirs = MainApp.getAppContext().getExternalFilesDirs(null);
75 if (dirs.length > 1) {
76 baseStoragePath = dirs[1];
77 }
78 return baseStoragePath.getAbsolutePath();
79 } else {
80 return baseStoragePath.getAbsolutePath() + "/" + MainApp.getDataFolder();
81 }
82 }
83
84 public static final String getSavePath(String accountName) {
85 //File sdCard = Environment.getExternalStorageDirectory();
86 //return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@");
87 // 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
88 //return getBaseStorePath().getAbsolutePath() + "/" + Uri.encode(accountName, "@");
89 return getBaseStorePathString() + "/" + Uri.encode(accountName, "@");
90 }
91
92 public static final String getDefaultSavePathFor(String accountName, OCFile file) {
93 return getSavePath(accountName) + file.getRemotePath();
94 }
95
96 public static final String getTemporalPath(String accountName) {
97 //File sdCard = Environment.getExternalStorageDirectory();
98 //return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/tmp/" + Uri.encode(accountName, "@");
99 // 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
100 //return getBaseStorePath().getAbsolutePath() + "/tmp/" + Uri.encode(accountName, "@");
101 return getBaseStorePathString() + "/tmp/" + Uri.encode(accountName, "@");
102 }
103
104 @SuppressLint("NewApi")
105 public static final long getUsableSpace(String accountName) {
106 //File savePath = Environment.getExternalStorageDirectory();
107 File savePath = getBaseStorePath();
108 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.GINGERBREAD) {
109 return savePath.getUsableSpace();
110
111 } else {
112 StatFs stats = new StatFs(savePath.getAbsolutePath());
113 return stats.getAvailableBlocks() * stats.getBlockSize();
114 }
115
116 }
117
118 public static final String getLogPath() {
119 return Environment.getExternalStorageDirectory() + File.separator + MainApp.getDataFolder() + File.separator + "log";
120 }
121
122 public static String getInstantUploadFilePath(Context context, String fileName) {
123 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
124 String uploadPathdef = context.getString(R.string.instant_upload_path);
125 String uploadPath = pref.getString("instant_upload_path", uploadPathdef);
126 String value = uploadPath + OCFile.PATH_SEPARATOR + (fileName == null ? "" : fileName);
127 return value;
128 }
129
130 /**
131 * Gets the composed path when video is or must be stored
132 * @param context
133 * @param fileName: video file name
134 * @return String: video file path composed
135 */
136 public static String getInstantVideoUploadFilePath(Context context, String fileName) {
137 SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context);
138 String uploadVideoPathdef = context.getString(R.string.instant_upload_path);
139 String uploadVideoPath = pref.getString("instant_video_upload_path", uploadVideoPathdef);
140 String value = uploadVideoPath + OCFile.PATH_SEPARATOR + (fileName == null ? "" : fileName);
141 return value;
142 }
143
144 public static String getParentPath(String remotePath) {
145 String parentPath = new File(remotePath).getParent();
146 parentPath = parentPath.endsWith(OCFile.PATH_SEPARATOR) ? parentPath : parentPath + OCFile.PATH_SEPARATOR;
147 return parentPath;
148 }
149
150 /**
151 * Creates and populates a new {@link OCFile} object with the data read from the server.
152 *
153 * @param remote remote file read from the server (remote file or folder).
154 * @return New OCFile instance representing the remote resource described by remote.
155 */
156 public static OCFile fillOCFile(RemoteFile remote) {
157 OCFile file = new OCFile(remote.getRemotePath());
158 file.setCreationTimestamp(remote.getCreationTimestamp());
159 file.setFileLength(remote.getLength());
160 file.setMimetype(remote.getMimeType());
161 file.setModificationTimestamp(remote.getModifiedTimestamp());
162 file.setEtag(remote.getEtag());
163 file.setPermissions(remote.getPermissions());
164 file.setRemoteId(remote.getRemoteId());
165 return file;
166 }
167
168 /**
169 * Creates and populates a new {@link RemoteFile} object with the data read from an {@link OCFile}.
170 *
171 * @param ocFile OCFile
172 * @return New RemoteFile instance representing the resource described by ocFile.
173 */
174 public static RemoteFile fillRemoteFile(OCFile ocFile){
175 RemoteFile file = new RemoteFile(ocFile.getRemotePath());
176 file.setCreationTimestamp(ocFile.getCreationTimestamp());
177 file.setLength(ocFile.getFileLength());
178 file.setMimeType(ocFile.getMimetype());
179 file.setModifiedTimestamp(ocFile.getModificationTimestamp());
180 file.setEtag(ocFile.getEtag());
181 file.setPermissions(ocFile.getPermissions());
182 file.setRemoteId(ocFile.getRemoteId());
183 return file;
184 }
185
186 /**
187 * Sorts all filenames, regarding last user decision
188 */
189 public static Vector<OCFile> sortFolder(Vector<OCFile> files){
190 switch (mSortOrder){
191 case 0:
192 files = FileStorageUtils.sortByName(files);
193 break;
194 case 1:
195 files = FileStorageUtils.sortByDate(files);
196 break;
197 case 2:
198 // mFiles = FileStorageUtils.sortBySize(mSortAscending);
199 break;
200 }
201
202 return files;
203 }
204
205 /**
206 * Sorts list by Date
207 * @param files
208 */
209 public static Vector<OCFile> sortByDate(Vector<OCFile> files){
210 final Integer val;
211 if (mSortAscending){
212 val = 1;
213 } else {
214 val = -1;
215 }
216
217 Collections.sort(files, new Comparator<OCFile>() {
218 public int compare(OCFile o1, OCFile o2) {
219 if (o1.isFolder() && o2.isFolder()) {
220 Long obj1 = o1.getModificationTimestamp();
221 return val * obj1.compareTo(o2.getModificationTimestamp());
222 }
223 else if (o1.isFolder()) {
224 return -1;
225 } else if (o2.isFolder()) {
226 return 1;
227 } else if (o1.getModificationTimestamp() == 0 || o2.getModificationTimestamp() == 0){
228 return 0;
229 } else {
230 Long obj1 = o1.getModificationTimestamp();
231 return val * obj1.compareTo(o2.getModificationTimestamp());
232 }
233 }
234 });
235
236 return files;
237 }
238
239 // /**
240 // * Sorts list by Size
241 // * @param sortAscending true: ascending, false: descending
242 // */
243 // public static Vector<OCFile> sortBySize(Vector<OCFile> files){
244 // final Integer val;
245 // if (mSortAscending){
246 // val = 1;
247 // } else {
248 // val = -1;
249 // }
250 //
251 // Collections.sort(files, new Comparator<OCFile>() {
252 // public int compare(OCFile o1, OCFile o2) {
253 // if (o1.isFolder() && o2.isFolder()) {
254 // Long obj1 = getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o1)));
255 // return val * obj1.compareTo(getFolderSize(new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, o2))));
256 // }
257 // else if (o1.isFolder()) {
258 // return -1;
259 // } else if (o2.isFolder()) {
260 // return 1;
261 // } else if (o1.getFileLength() == 0 || o2.getFileLength() == 0){
262 // return 0;
263 // } else {
264 // Long obj1 = o1.getFileLength();
265 // return val * obj1.compareTo(o2.getFileLength());
266 // }
267 // }
268 // });
269 //
270 // return files;
271 // }
272
273 /**
274 * Sorts list by Name
275 * @param files files to sort
276 */
277 public static Vector<OCFile> sortByName(Vector<OCFile> files){
278 final Integer val;
279 if (mSortAscending){
280 val = 1;
281 } else {
282 val = -1;
283 }
284
285 Collections.sort(files, new Comparator<OCFile>() {
286 public int compare(OCFile o1, OCFile o2) {
287 if (o1.isFolder() && o2.isFolder()) {
288 return val * new AlphanumComparator().compare(o1, o2);
289 } else if (o1.isFolder()) {
290 return -1;
291 } else if (o2.isFolder()) {
292 return 1;
293 }
294 return val * new AlphanumComparator().compare(o1, o2);
295 }
296 });
297
298 return files;
299 }
300
301 /**
302 * Local Folder size
303 * @param dir File
304 * @return Size in bytes
305 */
306 public static long getFolderSize(File dir) {
307 if (dir.exists()) {
308 long result = 0;
309 File[] fileList = dir.listFiles();
310 for(int i = 0; i < fileList.length; i++) {
311 if(fileList[i].isDirectory()) {
312 result += getFolderSize(fileList[i]);
313 } else {
314 result += fileList[i].length();
315 }
316 }
317 return result;
318 }
319 return 0;
320 }
321
322 /**
323 * Mimetype String of a file
324 * @param path
325 * @return
326 */
327 public static String getMimeTypeFromName(String path) {
328 String extension = "";
329 int pos = path.lastIndexOf('.');
330 if (pos >= 0) {
331 extension = path.substring(pos + 1);
332 }
333 String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase());
334 return (result != null) ? result : "";
335 }
336
337 /**
338 * Scans the default location for saving local copies of files searching for
339 * a 'lost' file with the same full name as the {@link OCFile} received as
340 * parameter.
341 *
342 * This method helps to keep linked local copies of the files when the app is uninstalled, and then
343 * reinstalled in the device. OR after the cache of the app was deleted in system settings.
344 *
345 * The method is assuming that all the local changes in the file where synchronized in the past. This is dangerous,
346 * but assuming the contrary could lead to massive unnecessary synchronizations of downloaded file after deleting
347 * the app cache.
348 *
349 * This should be changed in the near future to avoid any chance of data loss, but we need to add some options
350 * to limit hard automatic synchronizations to wifi, unless the user wants otherwise.
351 *
352 * @param file File to associate a possible 'lost' local file.
353 * @param account Account holding file.
354 */
355 public static void searchForLocalFileInDefaultPath(OCFile file, Account account) {
356 if (file.getStoragePath() == null && !file.isFolder()) {
357 File f = new File(FileStorageUtils.getDefaultSavePathFor(account.name, file));
358 if (f.exists()) {
359 file.setStoragePath(f.getAbsolutePath());
360 file.setLastSyncDateForData(f.lastModified());
361 }
362 }
363 }
364
365 }