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 17package androidx.core.graphics; 18 19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 20 21import android.content.ContentResolver; 22import android.content.Context; 23import android.content.res.Resources; 24import android.net.Uri; 25import android.os.CancellationSignal; 26import android.os.ParcelFileDescriptor; 27import android.os.Process; 28import android.util.Log; 29 30import androidx.annotation.Nullable; 31import androidx.annotation.RequiresApi; 32import androidx.annotation.RestrictTo; 33 34import java.io.Closeable; 35import java.io.File; 36import java.io.FileInputStream; 37import java.io.FileOutputStream; 38import java.io.IOException; 39import java.io.InputStream; 40import java.nio.ByteBuffer; 41import java.nio.channels.FileChannel; 42 43/** 44 * Utility methods for TypefaceCompat. 45 * @hide 46 */ 47@RestrictTo(LIBRARY_GROUP) 48public class TypefaceCompatUtil { 49 private static final String TAG = "TypefaceCompatUtil"; 50 51 private TypefaceCompatUtil() {} // Do not instantiate. 52 53 private static final String CACHE_FILE_PREFIX = ".font"; 54 55 /** 56 * Creates a temp file. 57 * 58 * Returns null if failed to create temp file. 59 */ 60 @Nullable 61 public static File getTempFile(Context context) { 62 final String prefix = CACHE_FILE_PREFIX + Process.myPid() + "-" + Process.myTid() + "-"; 63 for (int i = 0; i < 100; ++i) { 64 final File file = new File(context.getCacheDir(), prefix + i); 65 try { 66 if (file.createNewFile()) { 67 return file; 68 } 69 } catch (IOException e) { 70 // ignore. Try next file. 71 } 72 } 73 return null; 74 } 75 76 /** 77 * Copy the file contents to the direct byte buffer. 78 */ 79 @Nullable 80 @RequiresApi(19) 81 private static ByteBuffer mmap(File file) { 82 try (FileInputStream fis = new FileInputStream(file)) { 83 FileChannel channel = fis.getChannel(); 84 final long size = channel.size(); 85 return channel.map(FileChannel.MapMode.READ_ONLY, 0, size); 86 } catch (IOException e) { 87 return null; 88 } 89 } 90 91 /** 92 * Copy the file contents to the direct byte buffer. 93 */ 94 @Nullable 95 @RequiresApi(19) 96 public static ByteBuffer mmap(Context context, CancellationSignal cancellationSignal, Uri uri) { 97 final ContentResolver resolver = context.getContentResolver(); 98 try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r", cancellationSignal)) { 99 if (pfd == null) { 100 return null; 101 } 102 try (FileInputStream fis = new FileInputStream(pfd.getFileDescriptor())) { 103 FileChannel channel = fis.getChannel(); 104 final long size = channel.size(); 105 return channel.map(FileChannel.MapMode.READ_ONLY, 0, size); 106 } 107 } catch (IOException e) { 108 return null; 109 } 110 } 111 112 /** 113 * Copy the resource contents to the direct byte buffer. 114 */ 115 @Nullable 116 @RequiresApi(19) 117 public static ByteBuffer copyToDirectBuffer(Context context, Resources res, int id) { 118 File tmpFile = getTempFile(context); 119 if (tmpFile == null) { 120 return null; 121 } 122 try { 123 if (!copyToFile(tmpFile, res, id)) { 124 return null; 125 } 126 return mmap(tmpFile); 127 } finally { 128 tmpFile.delete(); 129 } 130 } 131 132 /** 133 * Copy the input stream contents to file. 134 */ 135 public static boolean copyToFile(File file, InputStream is) { 136 FileOutputStream os = null; 137 try { 138 os = new FileOutputStream(file, false); 139 byte[] buffer = new byte[1024]; 140 int readLen; 141 while ((readLen = is.read(buffer)) != -1) { 142 os.write(buffer, 0, readLen); 143 } 144 return true; 145 } catch (IOException e) { 146 Log.e(TAG, "Error copying resource contents to temp file: " + e.getMessage()); 147 return false; 148 } finally { 149 closeQuietly(os); 150 } 151 } 152 153 /** 154 * Copy the resource contents to file. 155 */ 156 public static boolean copyToFile(File file, Resources res, int id) { 157 InputStream is = null; 158 try { 159 is = res.openRawResource(id); 160 return copyToFile(file, is); 161 } finally { 162 closeQuietly(is); 163 } 164 } 165 166 public static void closeQuietly(Closeable c) { 167 if (c != null) { 168 try { 169 c.close(); 170 } catch (IOException e) { 171 } 172 } 173 } 174} 175