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