Home | History | Annotate | Download | only in provider
      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 androidx.core.provider;
     18 
     19 import android.content.ContentProvider;
     20 import android.content.ContentUris;
     21 import android.content.ContentValues;
     22 import android.content.Context;
     23 import android.content.res.AssetManager;
     24 import android.database.Cursor;
     25 import android.database.MatrixCursor;
     26 import android.net.Uri;
     27 import android.os.ParcelFileDescriptor;
     28 
     29 import androidx.core.provider.FontsContractCompat.Columns;
     30 
     31 import java.io.File;
     32 import java.io.FileNotFoundException;
     33 import java.io.FileOutputStream;
     34 import java.io.IOException;
     35 import java.io.InputStream;
     36 import java.util.Collections;
     37 import java.util.HashMap;
     38 import java.util.Map;
     39 
     40 /**
     41  * Provides a test Content Provider implementing {@link FontsContractCompat}.
     42  */
     43 public class MockFontProvider extends ContentProvider {
     44     public static final String AUTHORITY = "androidx.core.provider.fonts.font";
     45 
     46     static final String[] FONT_FILES = {
     47             "samplefont.ttf", "large_a.ttf", "large_b.ttf", "large_c.ttf", "large_d.ttf"
     48     };
     49     public static final int INVALID_FONT_FILE_ID = -1;
     50     private static final int SAMPLE_FONT_FILE_0_ID = 0;
     51     private static final int LARGE_A_FILE_ID = 1;
     52     private static final int LARGE_B_FILE_ID = 2;
     53     private static final int LARGE_C_FILE_ID = 3;
     54     private static final int LARGE_D_FILE_ID = 4;
     55 
     56     static final String SINGLE_FONT_FAMILY_QUERY = "singleFontFamily";
     57     static final String SINGLE_FONT_FAMILY2_QUERY = "singleFontFamily2";
     58     static final String NOT_FOUND_QUERY = "notFound";
     59     static final String UNAVAILABLE_QUERY = "unavailable";
     60     static final String MALFORMED_QUERY = "malformed";
     61     static final String NOT_FOUND_SECOND_QUERY = "notFoundSecond";
     62     static final String NOT_FOUND_THIRD_QUERY = "notFoundThird";
     63     static final String NEGATIVE_ERROR_CODE_QUERY = "negativeCode";
     64     static final String MANDATORY_FIELDS_ONLY_QUERY = "mandatoryFields";
     65     static final String STYLE_TEST_QUERY = "styleTest";
     66     static final String INVALID_URI = "invalidURI";
     67 
     68     static class Font {
     69         Font(int id, int fileId, int ttcIndex, String varSettings, int weight, int italic,
     70                 int resultCode, boolean returnAllFields) {
     71             mId = id;
     72             mFileId = fileId;
     73             mTtcIndex = ttcIndex;
     74             mVarSettings = varSettings;
     75             mWeight = weight;
     76             mItalic = italic;
     77             mResultCode = resultCode;
     78             mReturnAllFields = returnAllFields;
     79         }
     80 
     81         public int getId() {
     82             return mId;
     83         }
     84 
     85         public int getTtcIndex() {
     86             return mTtcIndex;
     87         }
     88 
     89         public String getVarSettings() {
     90             return mVarSettings;
     91         }
     92 
     93         public int getWeight() {
     94             return mWeight;
     95         }
     96 
     97         public int getItalic() {
     98             return mItalic;
     99         }
    100 
    101         public int getResultCode() {
    102             return mResultCode;
    103         }
    104 
    105         public int getFileId() {
    106             return mFileId;
    107         }
    108 
    109         public boolean isReturnAllFields() {
    110             return mReturnAllFields;
    111         }
    112 
    113         private final int mId;
    114         private final int mFileId;
    115         private final int mTtcIndex;
    116         private final String mVarSettings;
    117         private final int mWeight;
    118         private final int mItalic;
    119         private final int mResultCode;
    120         private final boolean mReturnAllFields;
    121     };
    122 
    123     private static final Map<String, Font[]> QUERY_MAP;
    124     static {
    125         HashMap<String, Font[]> map = new HashMap<>();
    126         int id = 1;
    127 
    128         map.put(SINGLE_FONT_FAMILY_QUERY, new Font[] {
    129                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, "'wght' 100", 400, 0,
    130                         Columns.RESULT_CODE_OK, true),
    131         });
    132 
    133         map.put(SINGLE_FONT_FAMILY2_QUERY, new Font[] {
    134                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, "'wght' 100", 700, 1,
    135                         Columns.RESULT_CODE_OK, true),
    136         });
    137 
    138         map.put(NOT_FOUND_QUERY, new Font[] {
    139                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
    140         });
    141 
    142         map.put(UNAVAILABLE_QUERY, new Font[] {
    143                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_UNAVAILABLE, true),
    144         });
    145 
    146         map.put(MALFORMED_QUERY, new Font[] {
    147                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_MALFORMED_QUERY, true),
    148         });
    149 
    150         map.put(NOT_FOUND_SECOND_QUERY, new Font[] {
    151                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
    152                         true),
    153                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
    154         });
    155 
    156         map.put(NOT_FOUND_THIRD_QUERY, new Font[] {
    157                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
    158                         true),
    159                 new Font(0, 0, 0, null, 400, 0, Columns.RESULT_CODE_FONT_NOT_FOUND, true),
    160                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, Columns.RESULT_CODE_OK,
    161                         true),
    162         });
    163 
    164         map.put(NEGATIVE_ERROR_CODE_QUERY, new Font[] {
    165                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 700, 0, -5, true),
    166         });
    167 
    168         map.put(MANDATORY_FIELDS_ONLY_QUERY, new Font[] {
    169                 new Font(id++, SAMPLE_FONT_FILE_0_ID, 0, null, 400, 0,
    170                         Columns.RESULT_CODE_OK, false),
    171         });
    172 
    173         map.put(STYLE_TEST_QUERY, new Font[] {
    174                 new Font(id++, LARGE_A_FILE_ID, 0, null, 400, 0 /* normal */,
    175                         Columns.RESULT_CODE_OK, true),
    176                 new Font(id++, LARGE_B_FILE_ID, 0, null, 400, 1 /* italic */,
    177                         Columns.RESULT_CODE_OK, true),
    178                 new Font(id++, LARGE_C_FILE_ID, 0, null, 700, 0 /* normal */,
    179                         Columns.RESULT_CODE_OK, true),
    180                 new Font(id++, LARGE_D_FILE_ID, 0, null, 700, 1 /* italic */,
    181                         Columns.RESULT_CODE_OK, true),
    182         });
    183 
    184         map.put(INVALID_URI, new Font[] {
    185                 new Font(id++, INVALID_FONT_FILE_ID, 0, null, 400, 0,
    186                         Columns.RESULT_CODE_OK, true),
    187         });
    188 
    189         QUERY_MAP = Collections.unmodifiableMap(map);
    190     }
    191 
    192     private static Cursor buildCursor(Font[] in) {
    193         if (!in[0].mReturnAllFields) {
    194             MatrixCursor cursor = new MatrixCursor(new String[] { Columns._ID, Columns.FILE_ID });
    195             for (Font font : in) {
    196                 cursor.addRow(new Object[] { font.getId(), font.getFileId() });
    197             }
    198             return cursor;
    199         }
    200         MatrixCursor cursor = new MatrixCursor(new String[] {
    201                 Columns._ID, Columns.TTC_INDEX, Columns.VARIATION_SETTINGS, Columns.WEIGHT,
    202                 Columns.ITALIC, Columns.RESULT_CODE, Columns.FILE_ID});
    203         for (Font font : in) {
    204             cursor.addRow(
    205                     new Object[] { font.getId(), font.getTtcIndex(), font.getVarSettings(),
    206                     font.getWeight(), font.getItalic(), font.getResultCode(), font.getFileId() });
    207         }
    208         return cursor;
    209     }
    210 
    211     public static void prepareFontFiles(Context context) {
    212         final AssetManager mgr = context.getAssets();
    213         for (String file : FONT_FILES) {
    214             InputStream is = null;
    215             try {
    216                 is = mgr.open("fonts/" + file);
    217                 File copied = getCopiedFile(context, file);
    218                 File parent = copied.getParentFile();
    219                 if (!parent.isDirectory()) {
    220                     parent.mkdirs();
    221                     parent.setReadable(true, false);
    222                     parent.setExecutable(true, false);
    223                 }
    224                 copy(is, copied);
    225                 copied.setReadable(true, false);
    226             } catch (IOException e) {
    227                 throw new RuntimeException(e);
    228             } finally {
    229                 if (is != null) {
    230                     try {
    231                         is.close();
    232                     } catch (IOException e) {
    233                         // Do nothing.
    234                     }
    235                 }
    236             }
    237         }
    238     }
    239 
    240     /**
    241      * The caller is responsible for closing the given InputStream.
    242      */
    243     private static void copy(InputStream is, File file) throws IOException {
    244         FileOutputStream fos = null;
    245         try {
    246             fos = new FileOutputStream(file, false);
    247             byte[] buffer = new byte[1024];
    248             int readLen;
    249             while ((readLen = is.read(buffer)) != -1) {
    250                 fos.write(buffer, 0, readLen);
    251             }
    252         } finally {
    253             if (fos != null) {
    254                 fos.close();
    255             }
    256         }
    257     }
    258 
    259     public static void cleanUpFontFiles(Context context) {
    260         for (String file : FONT_FILES) {
    261             getCopiedFile(context, file).delete();
    262         }
    263     }
    264 
    265     public static File getCopiedFile(Context context, String path) {
    266         final File cacheDir = new File(context.getFilesDir(), "fontCache");
    267         return new File(cacheDir, path);
    268     }
    269 
    270     @Override
    271     public ParcelFileDescriptor openFile(Uri uri, String mode) {
    272         final int id = (int) ContentUris.parseId(uri);
    273         if (id < 0) {
    274             return null;
    275         }
    276         final File targetFile = getCopiedFile(getContext(), FONT_FILES[id]);
    277         try {
    278             return ParcelFileDescriptor.open(targetFile, ParcelFileDescriptor.MODE_READ_ONLY);
    279         } catch (FileNotFoundException e) {
    280             throw new RuntimeException(
    281                     "Failed to found font file. You might forget call prepareFontFiles in setUp");
    282         }
    283     }
    284 
    285     @Override
    286     public boolean onCreate() {
    287         return true;
    288     }
    289 
    290     @Override
    291     public String getType(Uri uri) {
    292         return "vnd.android.cursor.dir/vnd.android.provider.font";
    293     }
    294 
    295     @Override
    296     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
    297             String sortOrder) {
    298         return buildCursor(QUERY_MAP.get(selectionArgs[0]));
    299     }
    300 
    301     @Override
    302     public Uri insert(Uri uri, ContentValues values) {
    303         throw new UnsupportedOperationException("insert is not supported.");
    304     }
    305 
    306     @Override
    307     public int delete(Uri uri, String selection, String[] selectionArgs) {
    308         throw new UnsupportedOperationException("delete is not supported.");
    309     }
    310 
    311     @Override
    312     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    313         throw new UnsupportedOperationException("update is not supported.");
    314     }
    315 }
    316