ArchivesProvider.java revision 5a9d100fe3d5b596d94a387e2dc370b84624062a
1dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski/* 2dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * Copyright (C) 2015 The Android Open Source Project 3dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * 4dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * Licensed under the Apache License, Version 2.0 (the "License"); 5dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * you may not use this file except in compliance with the License. 6dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * You may obtain a copy of the License at 7dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * 8dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * http://www.apache.org/licenses/LICENSE-2.0 9dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * 10dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * Unless required by applicable law or agreed to in writing, software 11dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * distributed under the License is distributed on an "AS IS" BASIS, 12dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * See the License for the specific language governing permissions and 14dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * limitations under the License. 15dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski */ 16dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 17dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskipackage com.android.documentsui.archives; 18dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 19dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.content.Context; 20dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.content.res.AssetFileDescriptor; 21dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.content.res.Configuration; 22dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.database.ContentObserver; 23dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.database.Cursor; 24dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.database.MatrixCursor.RowBuilder; 255a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewskiimport android.database.MatrixCursor; 26dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.graphics.Point; 27dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.net.Uri; 285a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewskiimport android.os.Bundle; 29dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.os.CancellationSignal; 30dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.os.ParcelFileDescriptor; 31dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.provider.DocumentsContract.Document; 32dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.provider.DocumentsContract; 33dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.provider.DocumentsProvider; 34dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.support.annotation.Nullable; 35dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.util.Log; 36dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport android.util.LruCache; 37dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 38dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport com.android.internal.annotations.GuardedBy; 39dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport com.android.internal.util.Preconditions; 40dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 41dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport java.io.Closeable; 42dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport java.io.File; 43dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport java.io.FileNotFoundException; 44dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport java.util.HashMap; 45dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport java.util.Map; 46dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskiimport java.util.concurrent.locks.Lock; 47dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 48dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski/** 49dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * Provides basic implementation for creating, extracting and accessing 50dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * files within archives exposed by a document provider. 51dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * 52dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * <p>This class is thread safe. All methods can be called on any thread without 53dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * synchronization. 54dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski */ 55dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewskipublic class ArchivesProvider extends DocumentsProvider implements Closeable { 56dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public static final String AUTHORITY = "com.android.documentsui.archives"; 57dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 58dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski private static final String TAG = "ArchivesProvider"; 59dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski private static final int OPENED_ARCHIVES_CACHE_SIZE = 4; 60dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski private static final String[] ZIP_MIME_TYPES = { 61dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski "application/zip", "application/x-zip", "application/x-zip-compressed" 62dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski }; 63dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 64dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @GuardedBy("mArchives") 65dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski private final LruCache<Uri, Loader> mArchives = 66dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski new LruCache<Uri, Loader>(OPENED_ARCHIVES_CACHE_SIZE) { 67dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 68dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public void entryRemoved(boolean evicted, Uri key, 69dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Loader oldValue, Loader newValue) { 70dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski oldValue.getWriteLock().lock(); 71dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski try { 72dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski oldValue.get().close(); 73dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } catch (FileNotFoundException e) { 74dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Log.e(TAG, "Failed to close an archive as it no longer exists."); 75dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } finally { 76dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski oldValue.getWriteLock().unlock(); 77dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 78dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 79dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski }; 80dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 81dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 82dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public boolean onCreate() { 83dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return true; 84dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 85dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 86dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 87dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public Cursor queryRoots(String[] projection) { 88dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski throw new UnsupportedOperationException(); 89dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 90dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 91dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 92dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public Cursor queryChildDocuments(String documentId, @Nullable String[] projection, 93dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Nullable String sortOrder) 94dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski throws FileNotFoundException { 955a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski final ArchiveId archiveId = ArchiveId.fromDocumentId(documentId); 96dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Loader loader = null; 97dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski try { 98dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader = obtainInstance(documentId); 995a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski if (loader.mArchive == null) { 1005a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski final MatrixCursor cursor = new MatrixCursor( 1015a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski projection != null ? projection : Archive.DEFAULT_PROJECTION); 1025a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski // Return an empty cursor with EXTRA_LOADING, which shows spinner 1035a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski // in DocumentsUI. Once the archive is loaded, the notification will 1045a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski // be sent, and the directory reloaded. 1055a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski final Bundle bundle = new Bundle(); 1065a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski bundle.putBoolean(DocumentsContract.EXTRA_LOADING, true); 1075a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski cursor.setExtras(bundle); 1085a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski cursor.setNotificationUri(getContext().getContentResolver(), 1095a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski buildUriForArchive(archiveId.mArchiveUri)); 1105a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski return cursor; 1115a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski } 112dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return loader.get().queryChildDocuments(documentId, projection, sortOrder); 113dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } finally { 114dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski releaseInstance(loader); 115dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 116dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 117dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 118dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 119dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public String getDocumentType(String documentId) throws FileNotFoundException { 120dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final ArchiveId archiveId = ArchiveId.fromDocumentId(documentId); 121dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski if (archiveId.mPath.equals("/")) { 122dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return Document.MIME_TYPE_DIR; 123dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 124dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 125dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Loader loader = null; 126dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski try { 127dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader = obtainInstance(documentId); 128dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return loader.get().getDocumentType(documentId); 129dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } finally { 130dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski releaseInstance(loader); 131dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 132dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 133dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 134dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 135dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public boolean isChildDocument(String parentDocumentId, String documentId) { 136dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Loader loader = null; 137dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski try { 138dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader = obtainInstance(documentId); 139dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return loader.get().isChildDocument(parentDocumentId, documentId); 140dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } catch (FileNotFoundException e) { 141dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski throw new IllegalStateException(e); 142dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } finally { 143dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski releaseInstance(loader); 144dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 145dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 146dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 147dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 148dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public Cursor queryDocument(String documentId, @Nullable String[] projection) 149dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski throws FileNotFoundException { 150dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final ArchiveId archiveId = ArchiveId.fromDocumentId(documentId); 151dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski if (archiveId.mPath.equals("/")) { 152dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final MatrixCursor cursor = new MatrixCursor( 153dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski projection != null ? projection : Archive.DEFAULT_PROJECTION); 154dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final RowBuilder row = cursor.newRow(); 155dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski row.add(Document.COLUMN_DOCUMENT_ID, documentId); 1565a9d100fe3d5b596d94a387e2dc370b84624062aTomasz Mikolajewski row.add(Document.COLUMN_DISPLAY_NAME, "Archive"); // TODO: Translate. 157dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski row.add(Document.COLUMN_SIZE, 0); 158dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); 159dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return cursor; 160dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 161dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 162dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Loader loader = null; 163dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski try { 164dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader = obtainInstance(documentId); 165dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return loader.get().queryDocument(documentId, projection); 166dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } finally { 167dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski releaseInstance(loader); 168dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 169dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 170dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 171dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 172dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public ParcelFileDescriptor openDocument( 173dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski String documentId, String mode, final CancellationSignal signal) 174dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski throws FileNotFoundException { 175dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Loader loader = null; 176dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski try { 177dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader = obtainInstance(documentId); 178dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return loader.get().openDocument(documentId, mode, signal); 179dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } finally { 180dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski releaseInstance(loader); 181dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 182dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 183dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 184dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 185dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public AssetFileDescriptor openDocumentThumbnail( 186dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski String documentId, Point sizeHint, final CancellationSignal signal) 187dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski throws FileNotFoundException { 188dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Loader loader = null; 189dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski try { 190dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader = obtainInstance(documentId); 191dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return loader.get().openDocumentThumbnail(documentId, sizeHint, signal); 192dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } finally { 193dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski releaseInstance(loader); 194dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 195dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 196dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 197dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski /** 198dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * Returns true if the passed mime type is supported by the helper. 199dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski */ 200dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public static boolean isSupportedArchiveType(String mimeType) { 201dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski for (final String zipMimeType : ZIP_MIME_TYPES) { 202dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski if (zipMimeType.equals(mimeType)) { 203dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return true; 204dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 205dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 206dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return false; 207dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 208dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 209dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public static Uri buildUriForArchive(Uri archiveUri) { 210dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return DocumentsContract.buildDocumentUri( 211dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski AUTHORITY, new ArchiveId(archiveUri, "/").toDocumentId()); 212dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 213dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 214dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski /** 215dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * Closes the helper and disposes all existing archives. It will block until all ongoing 216dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski * operations on each opened archive are finished. 217dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski */ 218dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 219dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski // TODO: Wire close() to call(). 220dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public void close() { 221dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski synchronized (mArchives) { 222dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski mArchives.evictAll(); 223dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 224dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 225dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 226dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski private Loader obtainInstance(String documentId) throws FileNotFoundException { 227dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Loader loader; 228dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski synchronized (mArchives) { 229dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader = getInstanceUncheckedLocked(documentId); 230dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader.getReadLock().lock(); 231dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 232dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return loader; 233dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 234dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 235dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski private void releaseInstance(@Nullable Loader loader) { 236dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski if (loader != null) { 237dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski loader.getReadLock().unlock(); 238dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 239dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 240dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 241dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski private Loader getInstanceUncheckedLocked(String documentId) 242dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski throws FileNotFoundException { 243dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final ArchiveId id = ArchiveId.fromDocumentId(documentId); 244dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski if (mArchives.get(id.mArchiveUri) != null) { 245dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return mArchives.get(id.mArchiveUri); 246dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 247dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 248dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final Cursor cursor = getContext().getContentResolver().query( 249dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski id.mArchiveUri, new String[] { Document.COLUMN_MIME_TYPE }, null, null, null); 250dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski cursor.moveToFirst(); 251dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final String mimeType = cursor.getString(cursor.getColumnIndex( 252dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Document.COLUMN_MIME_TYPE)); 253dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski Preconditions.checkArgument(isSupportedArchiveType(mimeType)); 254dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final Uri notificationUri = cursor.getNotificationUri(); 255dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final Loader loader = new Loader(getContext(), id.mArchiveUri, notificationUri); 256dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 257dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski // Remove the instance from mArchives collection once the archive file changes. 258dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski if (notificationUri != null) { 259dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final LruCache<Uri, Loader> finalArchives = mArchives; 260dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski getContext().getContentResolver().registerContentObserver(notificationUri, 261dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski false, 262dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski new ContentObserver(null) { 263dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski @Override 264dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski public void onChange(boolean selfChange, Uri uri) { 265dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski synchronized (mArchives) { 266dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski final Loader currentLoader = mArchives.get(id.mArchiveUri); 267dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski if (currentLoader == loader) { 268dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski mArchives.remove(id.mArchiveUri); 269dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 270dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 271dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 272dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski }); 273dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 274dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski 275dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski mArchives.put(id.mArchiveUri, loader); 276dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski return loader; 277dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski } 278dbd6b8bd50ac660cb9eb83d0bd39712b65382797Tomasz Mikolajewski} 279