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 package android.provider.cts;
17 
18 import static android.provider.FontsContract.Columns;
19 
20 import android.content.ContentProvider;
21 import android.content.ContentUris;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.content.res.AssetManager;
25 import android.database.Cursor;
26 import android.database.MatrixCursor;
27 import android.net.Uri;
28 import android.os.ParcelFileDescriptor;
29 
30 import java.io.File;
31 import java.io.FileNotFoundException;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.nio.file.Files;
36 import java.nio.file.StandardCopyOption;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.Map;
40 
41 public class MockFontProvider extends ContentProvider {
42     final static String AUTHORITY = "android.provider.fonts.cts.font";
43 
44     final static String[] FONT_FILES = {
45         "samplefont1.ttf", "samplefont2.ttf", "samplefont.ttc",
46     };
47     private static final int SAMPLE_FONT_FILE_0_ID = 0;
48     private static final int SAMPLE_FONT_FILE_1_ID = 1;
49     private static final int SAMPLE_TTC_FONT_FILE_ID = 2;
50 
51     static final String SINGLE_FONT_FAMILY_QUERY = "singleFontFamily";
52     static final String ALL_ATTRIBUTE_VALUES_QUERY = "allAttributeValues";
53     static final String MULTIPLE_FAMILY_QUERY = "multipleFontFamily";
54     static final String NOT_FOUND_QUERY = "notFound";
55     static final String UNAVAILABLE_QUERY = "unavailable";
56     static final String MALFORMED_QUERY = "malformed";
57     static final String NOT_FOUND_SECOND_QUERY = "notFoundSecond";
58     static final String NOT_FOUND_THIRD_QUERY = "notFoundThird";
59     static final String NEGATIVE_ERROR_CODE_QUERY = "negativeCode";
60     static final String MANDATORY_FIELDS_ONLY_QUERY = "mandatoryFields";
61 
62     static class Font {
Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic, int resultCode, boolean returnAllFields)63         public Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic,
64                 int resultCode, boolean returnAllFields) {
65             mId = id;
66             mFileId = fileId;
67             mTtcIndex = ttcIndex;
68             mVarSettings = varSettings;
69             mWeight = weight;
70             mItalic = italic;
71             mResultCode = resultCode;
72             mReturnAllFields = returnAllFields;
73         }
74 
getId()75         public int getId() {
76             return mId;
77         }
78 
getTtcIndex()79         public int getTtcIndex() {
80             return mTtcIndex;
81         }
82 
getVarSettings()83         public String getVarSettings() {
84             return mVarSettings;
85         }
86 
getWeight()87         public int getWeight() {
88             return mWeight;
89         }
90 
getItalic()91         public int getItalic() {
92             return mItalic;
93         }
94 
getResultCode()95         public int getResultCode() {
96             return mResultCode;
97         }
98 
getFileId()99         public int getFileId() {
100             return mFileId;
101         }
102 
isReturnAllFields()103         public boolean isReturnAllFields() {
104             return mReturnAllFields;
105         }
106 
107         private int mId;
108         private int mFileId;
109         private int mTtcIndex;
110         private String mVarSettings;
111         private int mWeight;
112         private int mItalic;
113         private int mResultCode;
114         private final boolean mReturnAllFields;
115     };
116 
117     private static Map<String, Font[]> QUERY_MAP;
118     static {
119         HashMap<String, Font[]> map = new HashMap<>();
120         int id = 0;
121 
map.put(SINGLE_FONT_FAMILY_QUERY, new Font[] { new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true), })122         map.put(SINGLE_FONT_FAMILY_QUERY, new Font[] {
123             new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true),
124         });
125 
map.put(MULTIPLE_FAMILY_QUERY, new Font[] { new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true), new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 400, 1, Columns.RESULT_CODE_OK, true), new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK, true), new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 700, 1, Columns.RESULT_CODE_OK, true), })126         map.put(MULTIPLE_FAMILY_QUERY, new Font[] {
127             new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, true),
128             new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 400, 1, Columns.RESULT_CODE_OK, true),
129             new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK, true),
130             new Font(id++, SAMPLE_FONT_FILE_1_ID, 0, null, 700, 1, Columns.RESULT_CODE_OK, true),
131         });
132 
map.put(ALL_ATTRIBUTE_VALUES_QUERY, new Font[] { new Font(id++, SAMPLE_TTC_FONT_FILE_ID, 0, "'wght' 100", 400, 0, Columns.RESULT_CODE_OK, true), new Font(id++, SAMPLE_TTC_FONT_FILE_ID, 1, "'wght' 100", 700, 1, Columns.RESULT_CODE_OK, true), })133         map.put(ALL_ATTRIBUTE_VALUES_QUERY, new Font[] {
134                 new Font(id++, SAMPLE_TTC_FONT_FILE_ID, 0, "'wght' 100", 400, 0,
135                         Columns.RESULT_CODE_OK, true),
136                 new Font(id++, SAMPLE_TTC_FONT_FILE_ID, 1, "'wght' 100", 700, 1,
137                         Columns.RESULT_CODE_OK, true),
138         });
139 
map.put(NOT_FOUND_QUERY, new Font[] { new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true), })140         map.put(NOT_FOUND_QUERY, new Font[] {
141                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
142         });
143 
map.put(UNAVAILABLE_QUERY, new Font[] { new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_UNAVAILABLE, true), })144         map.put(UNAVAILABLE_QUERY, new Font[] {
145                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_UNAVAILABLE, true),
146         });
147 
map.put(MALFORMED_QUERY, new Font[] { new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_MALFORMED_QUERY, true), })148         map.put(MALFORMED_QUERY, new Font[] {
149                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_MALFORMED_QUERY, true),
150         });
151 
map.put(NOT_FOUND_SECOND_QUERY, new Font[] { new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK, true), new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true), })152         map.put(NOT_FOUND_SECOND_QUERY, new Font[] {
153                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
154                         true),
155                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
156         });
157 
map.put(NOT_FOUND_THIRD_QUERY, new Font[] { new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK, true), new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true), new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK, true), })158         map.put(NOT_FOUND_THIRD_QUERY, new Font[] {
159                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
160                         true),
161                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
162                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
163                         true),
164         });
165 
map.put(NEGATIVE_ERROR_CODE_QUERY, new Font[] { new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, -5, true), })166         map.put(NEGATIVE_ERROR_CODE_QUERY, new Font[] {
167                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, -5, true),
168         });
169 
map.put(MANDATORY_FIELDS_ONLY_QUERY, new Font[] { new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0, Columns.RESULT_CODE_OK, false), })170         map.put(MANDATORY_FIELDS_ONLY_QUERY, new Font[] {
171                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0,
172                         Columns.RESULT_CODE_OK, false),
173         });
174 
175 
176         QUERY_MAP = Collections.unmodifiableMap(map);
177     }
178 
buildCursor(Font[] in)179     private static Cursor buildCursor(Font[] in) {
180         if (!in[0].mReturnAllFields) {
181             MatrixCursor cursor = new MatrixCursor(new String[] { Columns._ID, Columns.FILE_ID });
182             for (Font font : in) {
183                 MatrixCursor.RowBuilder builder = cursor.newRow();
184                 builder.add(Columns._ID, font.getId());
185                 builder.add(Columns.FILE_ID, font.getFileId());
186             }
187             return cursor;
188         }
189         MatrixCursor cursor = new MatrixCursor(new String[] {
190                 Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.WEIGHT,
191                 Columns.ITALIC, Columns.RESULT_CODE, Columns.FILE_ID});
192         for (Font font : in) {
193             MatrixCursor.RowBuilder builder = cursor.newRow();
194             builder.add(Columns._ID, font.getId());
195             builder.add(Columns.FILE_ID, font.getFileId());
196             builder.add(Columns.TTC_INDEX, font.getTtcIndex());
197             builder.add(Columns.VARIATION_SETTINGS, font.getVarSettings());
198             builder.add(Columns.WEIGHT, font.getWeight());
199             builder.add(Columns.ITALIC, font.getItalic());
200             builder.add(Columns.RESULT_CODE, font.getResultCode());
201         }
202         return cursor;
203     }
204 
prepareFontFiles(Context context)205     public static void prepareFontFiles(Context context) {
206         final AssetManager mgr = context.getAssets();
207         for (String path : FONT_FILES) {
208             try (InputStream is = mgr.open(path)) {
209                 Files.copy(is, getCopiedFile(context, path).toPath(),
210                         StandardCopyOption.REPLACE_EXISTING);
211             } catch (IOException e) {
212                 throw new RuntimeException(e);
213             }
214         }
215     }
216 
cleanUpFontFiles(Context context)217     public static void cleanUpFontFiles(Context context) {
218         for (String file : FONT_FILES) {
219             getCopiedFile(context, file).delete();
220         }
221     }
222 
getCopiedFile(Context context, String path)223     public static File getCopiedFile(Context context, String path) {
224         return new File(context.getFilesDir(), path);
225     }
226 
227     @Override
openFile(Uri uri, String mode)228     public ParcelFileDescriptor openFile(Uri uri, String mode) {
229         final int id = (int) ContentUris.parseId(uri);
230         final File targetFile = getCopiedFile(getContext(), FONT_FILES[id]);
231         try {
232             return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
233         } catch (FileNotFoundException e) {
234             throw new RuntimeException(
235                     "Failed to find font file. Did you forget to call prepareFontFiles in setUp?");
236         }
237     }
238 
239     @Override
onCreate()240     public boolean onCreate() {
241         return true;
242     }
243 
244     @Override
getType(Uri uri)245     public String getType(Uri uri) {
246         return "vnd.android.cursor.dir/vnd.android.provider.cts.font";
247     }
248 
249     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)250     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
251             String sortOrder) {
252         return buildCursor(QUERY_MAP.get(selectionArgs[0]));
253     }
254 
255     @Override
insert(Uri uri, ContentValues values)256     public Uri insert(Uri uri, ContentValues values) {
257         throw new UnsupportedOperationException("insert is not supported.");
258     }
259 
260     @Override
delete(Uri uri, String selection, String[] selectionArgs)261     public int delete(Uri uri, String selection, String[] selectionArgs) {
262         throw new UnsupportedOperationException("delete is not supported.");
263     }
264 
265     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)266     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
267         throw new UnsupportedOperationException("update is not supported.");
268     }
269 }
270