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