1 /* 2 * Copyright (C) 2017 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.testing; 18 19 import android.annotation.NonNull; 20 import android.content.pm.ProviderInfo; 21 import android.database.Cursor; 22 import android.database.MatrixCursor; 23 import android.net.Uri; 24 import android.os.Bundle; 25 import android.os.CancellationSignal; 26 import android.os.ParcelFileDescriptor; 27 import android.provider.DocumentsContract.Document; 28 import android.provider.DocumentsProvider; 29 30 import com.android.documentsui.base.DocumentInfo; 31 32 import java.io.FileNotFoundException; 33 34 /** 35 * Test doubles of {@link DocumentsProvider} to isolate document providers. This is not registered 36 * or exposed through AndroidManifest, but only used locally. 37 */ 38 public class TestDocumentsProvider extends DocumentsProvider { 39 40 private String[] DOCUMENTS_PROJECTION = new String[] { 41 Document.COLUMN_DOCUMENT_ID, 42 Document.COLUMN_MIME_TYPE, 43 Document.COLUMN_DISPLAY_NAME, 44 Document.COLUMN_LAST_MODIFIED, 45 Document.COLUMN_FLAGS, 46 Document.COLUMN_SUMMARY, 47 Document.COLUMN_SIZE, 48 Document.COLUMN_ICON 49 }; 50 51 private Cursor mNextChildDocuments; 52 private Cursor mNextRecentDocuments; 53 TestDocumentsProvider(String authority)54 public TestDocumentsProvider(String authority) { 55 ProviderInfo info = new ProviderInfo(); 56 info.authority = authority; 57 attachInfoForTesting(null, info); 58 } 59 60 @Override refresh(Uri url, Bundle args, CancellationSignal signal)61 public boolean refresh(Uri url, Bundle args, CancellationSignal signal) { 62 return true; 63 } 64 65 @Override queryRoots(String[] projection)66 public Cursor queryRoots(String[] projection) throws FileNotFoundException { 67 return null; 68 } 69 70 @Override queryDocument(String documentId, String[] projection)71 public Cursor queryDocument(String documentId, String[] projection) 72 throws FileNotFoundException { 73 return null; 74 } 75 76 @Override queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)77 public Cursor queryChildDocuments(String parentDocumentId, String[] projection, 78 String sortOrder) throws FileNotFoundException { 79 return mNextChildDocuments; 80 } 81 82 @Override openDocument(String documentId, String mode, CancellationSignal signal)83 public ParcelFileDescriptor openDocument(String documentId, String mode, 84 CancellationSignal signal) throws FileNotFoundException { 85 return null; 86 } 87 88 @Override queryRecentDocuments(String rootId, String[] projection)89 public Cursor queryRecentDocuments(String rootId, String[] projection) { 90 return mNextRecentDocuments; 91 } 92 93 @Override querySearchDocuments(String rootId, String query, String[] projection)94 public Cursor querySearchDocuments(String rootId, String query, String[] projection) { 95 if (mNextChildDocuments != null) { 96 return filterCursorByString(mNextChildDocuments, query); 97 } 98 99 return mNextChildDocuments; 100 } 101 102 @Override onCreate()103 public boolean onCreate() { 104 return true; 105 } 106 107 /** 108 * Sets the next return value for {@link #queryChildDocuments(String, String[], String)}. 109 * @param docs docs to return for next query. 110 */ setNextChildDocumentsReturns(DocumentInfo... docs)111 public void setNextChildDocumentsReturns(DocumentInfo... docs) { 112 mNextChildDocuments = createDocumentsCursor(docs); 113 } 114 setNextRecentDocumentsReturns(DocumentInfo... docs)115 public void setNextRecentDocumentsReturns(DocumentInfo... docs) { 116 mNextRecentDocuments = createDocumentsCursor(docs); 117 } 118 createDocumentsCursor(DocumentInfo... docs)119 private Cursor createDocumentsCursor(DocumentInfo... docs) { 120 TestCursor cursor = new TestCursor(DOCUMENTS_PROJECTION); 121 for (DocumentInfo doc : docs) { 122 cursor.newRow() 123 .add(Document.COLUMN_DOCUMENT_ID, doc.documentId) 124 .add(Document.COLUMN_MIME_TYPE, doc.mimeType) 125 .add(Document.COLUMN_DISPLAY_NAME, doc.displayName) 126 .add(Document.COLUMN_LAST_MODIFIED, doc.lastModified) 127 .add(Document.COLUMN_FLAGS, doc.flags) 128 .add(Document.COLUMN_SUMMARY, doc.summary) 129 .add(Document.COLUMN_SIZE, doc.size) 130 .add(Document.COLUMN_ICON, doc.icon); 131 } 132 133 return cursor; 134 } 135 filterCursorByString(@onNull Cursor cursor, String query)136 private static Cursor filterCursorByString(@NonNull Cursor cursor, String query) { 137 final int count = cursor.getCount(); 138 final String[] columnNames = cursor.getColumnNames(); 139 140 final MatrixCursor resultCursor = new MatrixCursor(columnNames, count); 141 cursor.moveToPosition(-1); 142 for (int i = 0; i < count; i++) { 143 cursor.moveToNext(); 144 final int index = cursor.getColumnIndex(Document.COLUMN_DISPLAY_NAME); 145 if (!cursor.getString(index).contains(query)) { 146 continue; 147 } 148 149 final MatrixCursor.RowBuilder builder = resultCursor.newRow(); 150 final int columnCount = cursor.getColumnCount(); 151 for (int j = 0; j < columnCount; j++) { 152 final int type = cursor.getType(j); 153 switch (type) { 154 case Cursor.FIELD_TYPE_INTEGER: 155 builder.add(cursor.getLong(j)); 156 break; 157 158 case Cursor.FIELD_TYPE_STRING: 159 builder.add(cursor.getString(j)); 160 break; 161 162 default: 163 break; 164 } 165 } 166 } 167 cursor.moveToPosition(-1); 168 return resultCursor; 169 } 170 } 171