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