1/* 2 * Copyright (C) 2014 The Android Open Source Project 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 17package android.support.v4.provider; 18 19import android.content.ContentResolver; 20import android.content.Context; 21import android.content.Intent; 22import android.content.pm.PackageManager; 23import android.database.Cursor; 24import android.net.Uri; 25import android.provider.DocumentsContract; 26import android.text.TextUtils; 27import android.util.Log; 28 29class DocumentsContractApi19 { 30 private static final String TAG = "DocumentFile"; 31 32 public static boolean isDocumentUri(Context context, Uri self) { 33 return DocumentsContract.isDocumentUri(context, self); 34 } 35 36 public static String getName(Context context, Uri self) { 37 return queryForString(context, self, DocumentsContract.Document.COLUMN_DISPLAY_NAME, null); 38 } 39 40 private static String getRawType(Context context, Uri self) { 41 return queryForString(context, self, DocumentsContract.Document.COLUMN_MIME_TYPE, null); 42 } 43 44 public static String getType(Context context, Uri self) { 45 final String rawType = getRawType(context, self); 46 if (DocumentsContract.Document.MIME_TYPE_DIR.equals(rawType)) { 47 return null; 48 } else { 49 return rawType; 50 } 51 } 52 53 public static boolean isDirectory(Context context, Uri self) { 54 return DocumentsContract.Document.MIME_TYPE_DIR.equals(getRawType(context, self)); 55 } 56 57 public static boolean isFile(Context context, Uri self) { 58 final String type = getRawType(context, self); 59 if (DocumentsContract.Document.MIME_TYPE_DIR.equals(type) || TextUtils.isEmpty(type)) { 60 return false; 61 } else { 62 return true; 63 } 64 } 65 66 public static long lastModified(Context context, Uri self) { 67 return queryForLong(context, self, DocumentsContract.Document.COLUMN_LAST_MODIFIED, 0); 68 } 69 70 public static long length(Context context, Uri self) { 71 return queryForLong(context, self, DocumentsContract.Document.COLUMN_SIZE, 0); 72 } 73 74 public static boolean canRead(Context context, Uri self) { 75 // Ignore if grant doesn't allow read 76 if (context.checkCallingOrSelfUriPermission(self, Intent.FLAG_GRANT_READ_URI_PERMISSION) 77 != PackageManager.PERMISSION_GRANTED) { 78 return false; 79 } 80 81 // Ignore documents without MIME 82 if (TextUtils.isEmpty(getRawType(context, self))) { 83 return false; 84 } 85 86 return true; 87 } 88 89 public static boolean canWrite(Context context, Uri self) { 90 // Ignore if grant doesn't allow write 91 if (context.checkCallingOrSelfUriPermission(self, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 92 != PackageManager.PERMISSION_GRANTED) { 93 return false; 94 } 95 96 final String type = getRawType(context, self); 97 final int flags = queryForInt(context, self, DocumentsContract.Document.COLUMN_FLAGS, 0); 98 99 // Ignore documents without MIME 100 if (TextUtils.isEmpty(type)) { 101 return false; 102 } 103 104 // Deletable documents considered writable 105 if ((flags & DocumentsContract.Document.FLAG_SUPPORTS_DELETE) != 0) { 106 return true; 107 } 108 109 if (DocumentsContract.Document.MIME_TYPE_DIR.equals(type) 110 && (flags & DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE) != 0) { 111 // Directories that allow create considered writable 112 return true; 113 } else if (!TextUtils.isEmpty(type) 114 && (flags & DocumentsContract.Document.FLAG_SUPPORTS_WRITE) != 0) { 115 // Writable normal files considered writable 116 return true; 117 } 118 119 return false; 120 } 121 122 public static boolean delete(Context context, Uri self) { 123 return DocumentsContract.deleteDocument(context.getContentResolver(), self); 124 } 125 126 public static boolean exists(Context context, Uri self) { 127 final ContentResolver resolver = context.getContentResolver(); 128 129 Cursor c = null; 130 try { 131 c = resolver.query(self, new String[] { 132 DocumentsContract.Document.COLUMN_DOCUMENT_ID }, null, null, null); 133 return c.getCount() > 0; 134 } catch (Exception e) { 135 Log.w(TAG, "Failed query: " + e); 136 return false; 137 } finally { 138 closeQuietly(c); 139 } 140 } 141 142 private static String queryForString(Context context, Uri self, String column, 143 String defaultValue) { 144 final ContentResolver resolver = context.getContentResolver(); 145 146 Cursor c = null; 147 try { 148 c = resolver.query(self, new String[] { column }, null, null, null); 149 if (c.moveToFirst() && !c.isNull(0)) { 150 return c.getString(0); 151 } else { 152 return defaultValue; 153 } 154 } catch (Exception e) { 155 Log.w(TAG, "Failed query: " + e); 156 return defaultValue; 157 } finally { 158 closeQuietly(c); 159 } 160 } 161 162 private static int queryForInt(Context context, Uri self, String column, 163 int defaultValue) { 164 return (int) queryForLong(context, self, column, defaultValue); 165 } 166 167 private static long queryForLong(Context context, Uri self, String column, 168 long defaultValue) { 169 final ContentResolver resolver = context.getContentResolver(); 170 171 Cursor c = null; 172 try { 173 c = resolver.query(self, new String[] { column }, null, null, null); 174 if (c.moveToFirst() && !c.isNull(0)) { 175 return c.getLong(0); 176 } else { 177 return defaultValue; 178 } 179 } catch (Exception e) { 180 Log.w(TAG, "Failed query: " + e); 181 return defaultValue; 182 } finally { 183 closeQuietly(c); 184 } 185 } 186 187 private static void closeQuietly(AutoCloseable closeable) { 188 if (closeable != null) { 189 try { 190 closeable.close(); 191 } catch (RuntimeException rethrown) { 192 throw rethrown; 193 } catch (Exception ignored) { 194 } 195 } 196 } 197} 198