15fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey/* 25fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * Copyright (C) 2014 The Android Open Source Project 35fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * 45fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 55fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * you may not use this file except in compliance with the License. 65fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * You may obtain a copy of the License at 75fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * 85fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 95fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * 105fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 115fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 125fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * See the License for the specific language governing permissions and 145fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey * limitations under the License. 155fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey */ 165fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 175fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeypackage android.support.v4.provider; 185fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 195fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.content.ContentResolver; 205fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.content.Context; 215fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.content.Intent; 225fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.content.pm.PackageManager; 235fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.database.Cursor; 245fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.net.Uri; 255fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.provider.DocumentsContract; 265fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.text.TextUtils; 275fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyimport android.util.Log; 285fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 295fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkeyclass DocumentsContractApi19 { 305fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey private static final String TAG = "DocumentFile"; 315fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 325fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static boolean isDocumentUri(Context context, Uri self) { 335fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return DocumentsContract.isDocumentUri(context, self); 345fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 355fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 365fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static String getName(Context context, Uri self) { 375fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return queryForString(context, self, DocumentsContract.Document.COLUMN_DISPLAY_NAME, null); 385fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 395fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 405fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey private static String getRawType(Context context, Uri self) { 415fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return queryForString(context, self, DocumentsContract.Document.COLUMN_MIME_TYPE, null); 425fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 435fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 445fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static String getType(Context context, Uri self) { 455fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey final String rawType = getRawType(context, self); 465fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (DocumentsContract.Document.MIME_TYPE_DIR.equals(rawType)) { 475fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return null; 485fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } else { 495fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return rawType; 505fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 515fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 525fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 535fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static boolean isDirectory(Context context, Uri self) { 545fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return DocumentsContract.Document.MIME_TYPE_DIR.equals(getRawType(context, self)); 555fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 565fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 575fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static boolean isFile(Context context, Uri self) { 585fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey final String type = getRawType(context, self); 595fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (DocumentsContract.Document.MIME_TYPE_DIR.equals(type) || TextUtils.isEmpty(type)) { 605fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return false; 615fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } else { 625fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return true; 635fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 645fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 655fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 665fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static long lastModified(Context context, Uri self) { 675fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return queryForLong(context, self, DocumentsContract.Document.COLUMN_LAST_MODIFIED, 0); 685fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 695fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 705fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static long length(Context context, Uri self) { 715fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return queryForLong(context, self, DocumentsContract.Document.COLUMN_SIZE, 0); 725fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 735fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 745fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static boolean canRead(Context context, Uri self) { 755fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey // Ignore if grant doesn't allow read 765fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (context.checkCallingOrSelfUriPermission(self, Intent.FLAG_GRANT_READ_URI_PERMISSION) 775fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey != PackageManager.PERMISSION_GRANTED) { 785fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return false; 795fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 805fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 815fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey // Ignore documents without MIME 825fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (TextUtils.isEmpty(getRawType(context, self))) { 835fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return false; 845fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 855fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 865fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return true; 875fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 885fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 895fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static boolean canWrite(Context context, Uri self) { 905fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey // Ignore if grant doesn't allow write 915fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (context.checkCallingOrSelfUriPermission(self, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 925fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey != PackageManager.PERMISSION_GRANTED) { 935fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return false; 945fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 955fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 965fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey final String type = getRawType(context, self); 975fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey final int flags = queryForInt(context, self, DocumentsContract.Document.COLUMN_FLAGS, 0); 985fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 995fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey // Ignore documents without MIME 1005fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (TextUtils.isEmpty(type)) { 1015fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return false; 1025fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1035fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1045fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey // Deletable documents considered writable 1055fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if ((flags & DocumentsContract.Document.FLAG_SUPPORTS_DELETE) != 0) { 1065fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return true; 1075fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1085fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1095fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (DocumentsContract.Document.MIME_TYPE_DIR.equals(type) 1105fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey && (flags & DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE) != 0) { 1115fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey // Directories that allow create considered writable 1125fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return true; 1135fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } else if (!TextUtils.isEmpty(type) 1145fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey && (flags & DocumentsContract.Document.FLAG_SUPPORTS_WRITE) != 0) { 1155fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey // Writable normal files considered writable 1165fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return true; 1175fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1185fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1195fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return false; 1205fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1215fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1225fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static boolean delete(Context context, Uri self) { 1235fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return DocumentsContract.deleteDocument(context.getContentResolver(), self); 1245fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1255fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1265fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey public static boolean exists(Context context, Uri self) { 1275fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey final ContentResolver resolver = context.getContentResolver(); 1285fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1295fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey Cursor c = null; 1305fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey try { 1315fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey c = resolver.query(self, new String[] { 1325fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey DocumentsContract.Document.COLUMN_DOCUMENT_ID }, null, null, null); 1335fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return c.getCount() > 0; 1345fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } catch (Exception e) { 1355fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey Log.w(TAG, "Failed query: " + e); 1365fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return false; 1375fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } finally { 1385fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey closeQuietly(c); 1395fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1405fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1415fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1425fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey private static String queryForString(Context context, Uri self, String column, 1435fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey String defaultValue) { 1445fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey final ContentResolver resolver = context.getContentResolver(); 1455fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1465fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey Cursor c = null; 1475fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey try { 1485fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey c = resolver.query(self, new String[] { column }, null, null, null); 1495fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (c.moveToFirst() && !c.isNull(0)) { 1505fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return c.getString(0); 1515fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } else { 1525fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return defaultValue; 1535fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1545fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } catch (Exception e) { 1555fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey Log.w(TAG, "Failed query: " + e); 1565fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return defaultValue; 1575fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } finally { 1585fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey closeQuietly(c); 1595fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1605fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1615fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1625fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey private static int queryForInt(Context context, Uri self, String column, 1635fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey int defaultValue) { 1645fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return (int) queryForLong(context, self, column, defaultValue); 1655fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1665fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1675fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey private static long queryForLong(Context context, Uri self, String column, 1685fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey long defaultValue) { 1695fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey final ContentResolver resolver = context.getContentResolver(); 1705fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1715fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey Cursor c = null; 1725fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey try { 1735fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey c = resolver.query(self, new String[] { column }, null, null, null); 1745fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (c.moveToFirst() && !c.isNull(0)) { 1755fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return c.getLong(0); 1765fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } else { 1775fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return defaultValue; 1785fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1795fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } catch (Exception e) { 1805fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey Log.w(TAG, "Failed query: " + e); 1815fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey return defaultValue; 1825fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } finally { 1835fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey closeQuietly(c); 1845fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1855fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1865fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey 1875fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey private static void closeQuietly(AutoCloseable closeable) { 1885fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey if (closeable != null) { 1895fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey try { 1905fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey closeable.close(); 1915fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } catch (RuntimeException rethrown) { 1925fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey throw rethrown; 1935fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } catch (Exception ignored) { 1945fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1955fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1965fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey } 1975fdfbc2e02f46509474057e4366585f6a6062cb9Jeff Sharkey} 198