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