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