FileUtils.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. /*
  2. * Copyright (C) 2007-2008 OpenIntents.org
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.ipaulpro.afilechooser.utils;
  17. import android.content.ContentResolver;
  18. import android.content.ContentUris;
  19. import android.content.Context;
  20. import android.content.Intent;
  21. import android.database.Cursor;
  22. import android.database.DatabaseUtils;
  23. import android.graphics.Bitmap;
  24. import android.net.Uri;
  25. import android.os.Build;
  26. import android.os.Environment;
  27. import android.provider.DocumentsContract;
  28. import android.provider.MediaStore;
  29. import android.util.Log;
  30. import android.webkit.MimeTypeMap;
  31. import com.ianhanniballake.localstorage.LocalStorageProvider;
  32. import java.io.File;
  33. import java.io.FileFilter;
  34. import java.text.DecimalFormat;
  35. import java.util.Comparator;
  36. /**
  37. * @version 2009-07-03
  38. * @author Peli
  39. * @version 2013-12-11
  40. * @author paulburke (ipaulpro)
  41. */
  42. public class FileUtils {
  43. private FileUtils() {} //private constructor to enforce Singleton pattern
  44. /** TAG for log messages. */
  45. static final String TAG = "FileUtils";
  46. private static final boolean DEBUG = false; // Set to true to enable logging
  47. public static final String MIME_TYPE_AUDIO = "audio/*";
  48. public static final String MIME_TYPE_TEXT = "text/*";
  49. public static final String MIME_TYPE_IMAGE = "image/*";
  50. public static final String MIME_TYPE_VIDEO = "video/*";
  51. public static final String MIME_TYPE_APP = "application/*";
  52. public static final String HIDDEN_PREFIX = ".";
  53. /**
  54. * Gets the extension of a file name, like ".png" or ".jpg".
  55. *
  56. * @param uri
  57. * @return Extension including the dot("."); "" if there is no extension;
  58. * null if uri was null.
  59. */
  60. public static String getExtension(String uri) {
  61. if (uri == null) {
  62. return null;
  63. }
  64. int dot = uri.lastIndexOf(".");
  65. if (dot >= 0) {
  66. return uri.substring(dot);
  67. } else {
  68. // No extension.
  69. return "";
  70. }
  71. }
  72. /**
  73. * @return Whether the URI is a local one.
  74. */
  75. public static boolean isLocal(String url) {
  76. if (url != null && !url.startsWith("http://") && !url.startsWith("https://")) {
  77. return true;
  78. }
  79. return false;
  80. }
  81. /**
  82. * @return True if Uri is a MediaStore Uri.
  83. * @author paulburke
  84. */
  85. public static boolean isMediaUri(Uri uri) {
  86. return "media".equalsIgnoreCase(uri.getAuthority());
  87. }
  88. /**
  89. * Convert File into Uri.
  90. *
  91. * @param file
  92. * @return uri
  93. */
  94. public static Uri getUri(File file) {
  95. if (file != null) {
  96. return Uri.fromFile(file);
  97. }
  98. return null;
  99. }
  100. /**
  101. * Returns the path only (without file name).
  102. *
  103. * @param file
  104. * @return
  105. */
  106. public static File getPathWithoutFilename(File file) {
  107. if (file != null) {
  108. if (file.isDirectory()) {
  109. // no file to be split off. Return everything
  110. return file;
  111. } else {
  112. String filename = file.getName();
  113. String filepath = file.getAbsolutePath();
  114. // Construct path without file name.
  115. String pathwithoutname = filepath.substring(0,
  116. filepath.length() - filename.length());
  117. if (pathwithoutname.endsWith("/")) {
  118. pathwithoutname = pathwithoutname.substring(0, pathwithoutname.length() - 1);
  119. }
  120. return new File(pathwithoutname);
  121. }
  122. }
  123. return null;
  124. }
  125. /**
  126. * @return The MIME type for the given file.
  127. */
  128. public static String getMimeType(File file) {
  129. String extension = getExtension(file.getName());
  130. if (extension.length() > 0)
  131. return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1));
  132. return "application/octet-stream";
  133. }
  134. /**
  135. * @return The MIME type for the give Uri.
  136. */
  137. public static String getMimeType(Context context, Uri uri) {
  138. File file = new File(getPath(context, uri));
  139. return getMimeType(file);
  140. }
  141. /**
  142. * @param uri The Uri to check.
  143. * @return Whether the Uri authority is {@link LocalStorageProvider}.
  144. * @author paulburke
  145. */
  146. public static boolean isLocalStorageDocument(Uri uri) {
  147. return LocalStorageProvider.AUTHORITY.equals(uri.getAuthority());
  148. }
  149. /**
  150. * @param uri The Uri to check.
  151. * @return Whether the Uri authority is ExternalStorageProvider.
  152. * @author paulburke
  153. */
  154. public static boolean isExternalStorageDocument(Uri uri) {
  155. return "com.android.externalstorage.documents".equals(uri.getAuthority());
  156. }
  157. /**
  158. * @param uri The Uri to check.
  159. * @return Whether the Uri authority is DownloadsProvider.
  160. * @author paulburke
  161. */
  162. public static boolean isDownloadsDocument(Uri uri) {
  163. return "com.android.providers.downloads.documents".equals(uri.getAuthority());
  164. }
  165. /**
  166. * @param uri The Uri to check.
  167. * @return Whether the Uri authority is MediaProvider.
  168. * @author paulburke
  169. */
  170. public static boolean isMediaDocument(Uri uri) {
  171. return "com.android.providers.media.documents".equals(uri.getAuthority());
  172. }
  173. /**
  174. * @param uri The Uri to check.
  175. * @return Whether the Uri authority is Google Photos.
  176. */
  177. public static boolean isGooglePhotosUri(Uri uri) {
  178. return "com.google.android.apps.photos.content".equals(uri.getAuthority());
  179. }
  180. /**
  181. * Get the value of the data column for this Uri. This is useful for
  182. * MediaStore Uris, and other file-based ContentProviders.
  183. *
  184. * @param context The context.
  185. * @param uri The Uri to query.
  186. * @param selection (Optional) Filter used in the query.
  187. * @param selectionArgs (Optional) Selection arguments used in the query.
  188. * @return The value of the _data column, which is typically a file path.
  189. * @author paulburke
  190. */
  191. public static String getDataColumn(Context context, Uri uri, String selection,
  192. String[] selectionArgs) {
  193. Cursor cursor = null;
  194. final String column = "_data";
  195. final String[] projection = {
  196. column
  197. };
  198. try {
  199. cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
  200. null);
  201. if (cursor != null && cursor.moveToFirst()) {
  202. if (DEBUG)
  203. DatabaseUtils.dumpCursor(cursor);
  204. final int column_index = cursor.getColumnIndexOrThrow(column);
  205. return cursor.getString(column_index);
  206. }
  207. } finally {
  208. if (cursor != null)
  209. cursor.close();
  210. }
  211. return null;
  212. }
  213. /**
  214. * Get a file path from a Uri. This will get the the path for Storage Access
  215. * Framework Documents, as well as the _data field for the MediaStore and
  216. * other file-based ContentProviders.<br>
  217. * <br>
  218. * Callers should check whether the path is local before assuming it
  219. * represents a local file.
  220. *
  221. * @param context The context.
  222. * @param uri The Uri to query.
  223. * @see #isLocal(String)
  224. * @see #getFile(Context, Uri)
  225. * @author paulburke
  226. */
  227. public static String getPath(final Context context, final Uri uri) {
  228. if (DEBUG)
  229. Log.d(TAG + " File -",
  230. "Authority: " + uri.getAuthority() +
  231. ", Fragment: " + uri.getFragment() +
  232. ", Port: " + uri.getPort() +
  233. ", Query: " + uri.getQuery() +
  234. ", Scheme: " + uri.getScheme() +
  235. ", Host: " + uri.getHost() +
  236. ", Segments: " + uri.getPathSegments().toString()
  237. );
  238. final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
  239. // DocumentProvider
  240. if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
  241. // LocalStorageProvider
  242. if (isLocalStorageDocument(uri)) {
  243. // The path is the id
  244. return DocumentsContract.getDocumentId(uri);
  245. }
  246. // ExternalStorageProvider
  247. else if (isExternalStorageDocument(uri)) {
  248. final String docId = DocumentsContract.getDocumentId(uri);
  249. final String[] split = docId.split(":");
  250. final String type = split[0];
  251. if ("primary".equalsIgnoreCase(type)) {
  252. return Environment.getExternalStorageDirectory() + "/" + split[1];
  253. }
  254. else
  255. {
  256. final int splitIndex = docId.indexOf(':', 1);
  257. final String tag = docId.substring(0, splitIndex);
  258. final String path = docId.substring(splitIndex + 1);
  259. String nonPrimaryVolume = getPathToNonPrimaryVolume(context, tag);
  260. if (nonPrimaryVolume != null)
  261. {
  262. String result = nonPrimaryVolume + "/" + path;
  263. File file = new File(result);
  264. if (file.exists() && file.canRead())
  265. {
  266. return result;
  267. }
  268. }
  269. }
  270. // TODO handle non-primary volumes
  271. }
  272. // DownloadsProvider
  273. else if (isDownloadsDocument(uri)) {
  274. final String id = DocumentsContract.getDocumentId(uri);
  275. final Uri contentUri = ContentUris.withAppendedId(
  276. Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
  277. return getDataColumn(context, contentUri, null, null);
  278. }
  279. // MediaProvider
  280. else if (isMediaDocument(uri)) {
  281. final String docId = DocumentsContract.getDocumentId(uri);
  282. final String[] split = docId.split(":");
  283. final String type = split[0];
  284. Uri contentUri = null;
  285. if ("image".equals(type)) {
  286. contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
  287. } else if ("video".equals(type)) {
  288. contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
  289. } else if ("audio".equals(type)) {
  290. contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
  291. }
  292. final String selection = "_id=?";
  293. final String[] selectionArgs = new String[] {
  294. split[1]
  295. };
  296. return getDataColumn(context, contentUri, selection, selectionArgs);
  297. }
  298. }
  299. // MediaStore (and general)
  300. else if ("content".equalsIgnoreCase(uri.getScheme())) {
  301. // Return the remote address
  302. if (isGooglePhotosUri(uri))
  303. return uri.getLastPathSegment();
  304. return getDataColumn(context, uri, null, null);
  305. }
  306. // File
  307. else if ("file".equalsIgnoreCase(uri.getScheme())) {
  308. return uri.getPath();
  309. }
  310. return null;
  311. }
  312. public static String getPathToNonPrimaryVolume(Context context, String tag)
  313. {
  314. File[] volumes = context.getExternalCacheDirs();
  315. if (volumes != null)
  316. {
  317. for (File volume : volumes)
  318. {
  319. if (volume != null)
  320. {
  321. String path = volume.getAbsolutePath();
  322. if (path != null)
  323. {
  324. int index = path.indexOf(tag);
  325. if (index != -1)
  326. {
  327. return path.substring(0, index) + tag;
  328. }
  329. }
  330. }
  331. }
  332. }
  333. return null;
  334. }
  335. /**
  336. * Convert Uri into File, if possible.
  337. *
  338. * @return file A local file that the Uri was pointing to, or null if the
  339. * Uri is unsupported or pointed to a remote resource.
  340. * @see #getPath(Context, Uri)
  341. * @author paulburke
  342. */
  343. public static File getFile(Context context, Uri uri) {
  344. if (uri != null) {
  345. String path = getPath(context, uri);
  346. if (path != null && isLocal(path)) {
  347. return new File(path);
  348. }
  349. }
  350. return null;
  351. }
  352. /**
  353. * Get the file size in a human-readable string.
  354. *
  355. * @param size
  356. * @return
  357. * @author paulburke
  358. */
  359. public static String getReadableFileSize(int size) {
  360. final int BYTES_IN_KILOBYTES = 1024;
  361. final DecimalFormat dec = new DecimalFormat("###.#");
  362. final String KILOBYTES = " KB";
  363. final String MEGABYTES = " MB";
  364. final String GIGABYTES = " GB";
  365. float fileSize = 0;
  366. String suffix = KILOBYTES;
  367. if (size > BYTES_IN_KILOBYTES) {
  368. fileSize = size / BYTES_IN_KILOBYTES;
  369. if (fileSize > BYTES_IN_KILOBYTES) {
  370. fileSize = fileSize / BYTES_IN_KILOBYTES;
  371. if (fileSize > BYTES_IN_KILOBYTES) {
  372. fileSize = fileSize / BYTES_IN_KILOBYTES;
  373. suffix = GIGABYTES;
  374. } else {
  375. suffix = MEGABYTES;
  376. }
  377. }
  378. }
  379. return String.valueOf(dec.format(fileSize) + suffix);
  380. }
  381. /**
  382. * Attempt to retrieve the thumbnail of given File from the MediaStore. This
  383. * should not be called on the UI thread.
  384. *
  385. * @param context
  386. * @param file
  387. * @return
  388. * @author paulburke
  389. */
  390. public static Bitmap getThumbnail(Context context, File file) {
  391. return getThumbnail(context, getUri(file), getMimeType(file));
  392. }
  393. /**
  394. * Attempt to retrieve the thumbnail of given Uri from the MediaStore. This
  395. * should not be called on the UI thread.
  396. *
  397. * @param context
  398. * @param uri
  399. * @return
  400. * @author paulburke
  401. */
  402. public static Bitmap getThumbnail(Context context, Uri uri) {
  403. return getThumbnail(context, uri, getMimeType(context, uri));
  404. }
  405. /**
  406. * Attempt to retrieve the thumbnail of given Uri from the MediaStore. This
  407. * should not be called on the UI thread.
  408. *
  409. * @param context
  410. * @param uri
  411. * @param mimeType
  412. * @return
  413. * @author paulburke
  414. */
  415. public static Bitmap getThumbnail(Context context, Uri uri, String mimeType) {
  416. if (DEBUG)
  417. Log.d(TAG, "Attempting to get thumbnail");
  418. if (!isMediaUri(uri)) {
  419. Log.e(TAG, "You can only retrieve thumbnails for images and videos.");
  420. return null;
  421. }
  422. Bitmap bm = null;
  423. if (uri != null) {
  424. final ContentResolver resolver = context.getContentResolver();
  425. Cursor cursor = null;
  426. try {
  427. cursor = resolver.query(uri, null, null, null, null);
  428. if (cursor.moveToFirst()) {
  429. final int id = cursor.getInt(0);
  430. if (DEBUG)
  431. Log.d(TAG, "Got thumb ID: " + id);
  432. if (mimeType.contains("video")) {
  433. bm = MediaStore.Video.Thumbnails.getThumbnail(
  434. resolver,
  435. id,
  436. MediaStore.Video.Thumbnails.MINI_KIND,
  437. null);
  438. }
  439. else if (mimeType.contains(FileUtils.MIME_TYPE_IMAGE)) {
  440. bm = MediaStore.Images.Thumbnails.getThumbnail(
  441. resolver,
  442. id,
  443. MediaStore.Images.Thumbnails.MINI_KIND,
  444. null);
  445. }
  446. }
  447. } catch (Exception e) {
  448. if (DEBUG)
  449. Log.e(TAG, "getThumbnail", e);
  450. } finally {
  451. if (cursor != null)
  452. cursor.close();
  453. }
  454. }
  455. return bm;
  456. }
  457. /**
  458. * File and folder comparator. TODO Expose sorting option method
  459. *
  460. * @author paulburke
  461. */
  462. public static Comparator<File> sComparator = new Comparator<File>() {
  463. @Override
  464. public int compare(File f1, File f2) {
  465. // Sort alphabetically by lower case, which is much cleaner
  466. return f1.getName().toLowerCase().compareTo(
  467. f2.getName().toLowerCase());
  468. }
  469. };
  470. /**
  471. * File (not directories) filter.
  472. *
  473. * @author paulburke
  474. */
  475. public static FileFilter sFileFilter = new FileFilter() {
  476. @Override
  477. public boolean accept(File file) {
  478. final String fileName = file.getName();
  479. // Return files only (not directories) and skip hidden files
  480. return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX);
  481. }
  482. };
  483. /**
  484. * Folder (directories) filter.
  485. *
  486. * @author paulburke
  487. */
  488. public static FileFilter sDirFilter = new FileFilter() {
  489. @Override
  490. public boolean accept(File file) {
  491. final String fileName = file.getName();
  492. // Return directories only and skip hidden directories
  493. return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX);
  494. }
  495. };
  496. /**
  497. * Get the Intent for selecting content to be used in an Intent Chooser.
  498. *
  499. * @return The intent for opening a file with Intent.createChooser()
  500. * @author paulburke
  501. */
  502. public static Intent createGetContentIntent() {
  503. // Implicitly allow the user to select a particular kind of data
  504. final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
  505. // The MIME data type filter
  506. intent.setType("*/*");
  507. // Only return URIs that can be opened with ContentResolver
  508. intent.addCategory(Intent.CATEGORY_OPENABLE);
  509. return intent;
  510. }
  511. }