1 /* 2 * Copyright (C) 2016 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 17 package com.android.documentsui.archives; 18 19 import com.android.documentsui.tests.R; 20 21 import android.content.Context; 22 import android.content.res.AssetFileDescriptor; 23 import android.database.Cursor; 24 import android.database.MatrixCursor; 25 import android.database.MatrixCursor.RowBuilder; 26 import android.net.Uri; 27 import android.os.CancellationSignal; 28 import android.os.ParcelFileDescriptor; 29 import android.provider.DocumentsContract; 30 import android.provider.DocumentsContract.Document; 31 import android.provider.DocumentsContract.Root; 32 import android.provider.DocumentsProvider; 33 import android.webkit.MimeTypeMap; 34 35 import libcore.io.IoUtils; 36 37 import java.io.File; 38 import java.io.FileNotFoundException; 39 import java.util.HashMap; 40 import java.util.Map; 41 import java.util.concurrent.ExecutorService; 42 import java.util.concurrent.Executors; 43 44 45 public class ResourcesProvider extends DocumentsProvider { 46 public static final String AUTHORITY = "com.android.documentsui.archives.resourcesprovider"; 47 48 private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { 49 Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID 50 }; 51 private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { 52 Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME, 53 Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE, 54 }; 55 56 private static final Map<String, Integer> RESOURCES = new HashMap<>(); 57 static { 58 RESOURCES.put("archive.zip", R.raw.archive); 59 RESOURCES.put("empty_dirs.zip", R.raw.empty_dirs); 60 RESOURCES.put("no_dirs.zip", R.raw.no_dirs); 61 RESOURCES.put("broken.zip", R.raw.broken); 62 } 63 64 private ExecutorService mExecutor = null; 65 private TestUtils mTestUtils = null; 66 67 @Override onCreate()68 public boolean onCreate() { 69 mExecutor = Executors.newSingleThreadExecutor(); 70 mTestUtils = new TestUtils(getContext(), getContext(), mExecutor); 71 return true; 72 } 73 74 @Override queryRoots(String[] projection)75 public Cursor queryRoots(String[] projection) throws FileNotFoundException { 76 final MatrixCursor result = new MatrixCursor(projection != null ? projection 77 : DEFAULT_ROOT_PROJECTION); 78 final RowBuilder row = result.newRow(); 79 row.add(Root.COLUMN_ROOT_ID, "root-id"); 80 row.add(Root.COLUMN_FLAGS, 0); 81 row.add(Root.COLUMN_TITLE, "ResourcesProvider"); 82 row.add(Root.COLUMN_DOCUMENT_ID, "root-document-id"); 83 return result; 84 } 85 86 @Override queryDocument(String documentId, String[] projection)87 public Cursor queryDocument(String documentId, String[] projection) 88 throws FileNotFoundException { 89 final MatrixCursor result = new MatrixCursor(projection != null ? projection 90 : DEFAULT_DOCUMENT_PROJECTION); 91 if ("root-document-id".equals(documentId)) { 92 final RowBuilder row = result.newRow(); 93 row.add(Document.COLUMN_DOCUMENT_ID, "root-document-id"); 94 row.add(Document.COLUMN_FLAGS, 0); 95 row.add(Document.COLUMN_DISPLAY_NAME, "ResourcesProvider"); 96 row.add(Document.COLUMN_SIZE, 0); 97 row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); 98 return result; 99 } 100 101 includeDocument(result, documentId); 102 return result; 103 } 104 105 @Override queryChildDocuments( String parentDocumentId, String[] projection, String sortOrder)106 public Cursor queryChildDocuments( 107 String parentDocumentId, String[] projection, String sortOrder) 108 throws FileNotFoundException { 109 if (!"root-document-id".equals(parentDocumentId)) { 110 throw new FileNotFoundException(); 111 } 112 113 final MatrixCursor result = new MatrixCursor(projection != null ? projection 114 : DEFAULT_DOCUMENT_PROJECTION); 115 for (String documentId : RESOURCES.keySet()) { 116 includeDocument(result, documentId); 117 } 118 return result; 119 } 120 121 @Override openDocument(String docId, String mode, CancellationSignal signal)122 public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal) 123 throws FileNotFoundException { 124 final Integer resourceId = RESOURCES.get(docId); 125 if (resourceId == null) { 126 throw new FileNotFoundException(); 127 } 128 return mTestUtils.getSeekableDescriptor(resourceId); 129 } 130 includeDocument(MatrixCursor result, String documentId)131 void includeDocument(MatrixCursor result, String documentId) throws FileNotFoundException { 132 final Integer resourceId = RESOURCES.get(documentId); 133 if (resourceId == null) { 134 throw new FileNotFoundException(); 135 } 136 137 AssetFileDescriptor fd = null; 138 try { 139 fd = getContext().getResources().openRawResourceFd(resourceId); 140 final RowBuilder row = result.newRow(); 141 row.add(Document.COLUMN_DOCUMENT_ID, documentId); 142 row.add(Document.COLUMN_FLAGS, 0); 143 row.add(Document.COLUMN_DISPLAY_NAME, documentId); 144 145 final int lastDot = documentId.lastIndexOf('.'); 146 assert(lastDot > 0); 147 final String extension = documentId.substring(lastDot + 1).toLowerCase(); 148 final String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 149 150 row.add(Document.COLUMN_MIME_TYPE, mimeType); 151 row.add(Document.COLUMN_SIZE, fd.getLength()); 152 } 153 finally { 154 IoUtils.closeQuietly(fd); 155 } 156 } 157 } 158