1 /* 2 * Copyright (C) 2013 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.example.android.justforus; 18 19 import android.content.ContentProvider; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.content.res.AssetFileDescriptor; 23 import android.content.res.AssetManager; 24 import android.database.Cursor; 25 import android.database.MatrixCursor; 26 import android.net.Uri; 27 import android.provider.OpenableColumns; 28 import android.util.Log; 29 30 import java.io.FileNotFoundException; 31 import java.io.IOException; 32 33 import static java.net.URLConnection.guessContentTypeFromName; 34 35 /** 36 * Generic content provider, which makes any files available in this app's "assets" directory 37 * available publicly. 38 * 39 * <p>To use, add the following to your AndroidManifest.xml: 40 * 41 * <code><pre> 42 * <provider 43 * android:name=".AssetProvider" 44 * android:authorities="[YOUR CONTENT PROVIDER DOMAIN HERE]" 45 * android:grantUriPermissions="true" 46 * android:exported="true"/> 47 * </pre></code> 48 */ 49 public class AssetProvider extends ContentProvider { 50 51 /** 52 * Content provider authority that identifies data that is offered by this 53 * {@link AssetProvider}. 54 */ 55 public static String CONTENT_URI = "com.example.android.justforus"; 56 57 private static final String TAG = "AssetProvider"; 58 59 AssetManager mAssets; 60 61 @Override onCreate()62 public boolean onCreate() { 63 Context ctx = getContext(); 64 if (ctx == null) { 65 // Context not available. Give up. 66 return false; 67 } 68 mAssets = ctx.getAssets(); 69 return true; 70 } 71 72 @Override getType(Uri uri)73 public String getType(Uri uri){ 74 // Returns the MIME type for the selected URI, in conformance with the ContentProvider 75 // interface. Looks up the file indicated by /res/assets/{uri.path}, and returns the MIME 76 // type for that file as guessed by the URLConnection class. 77 78 // Setup 79 String path = uri.getLastPathSegment(); 80 81 // Check if file exists 82 if (!fileExists(path)) { 83 return null; 84 } 85 86 // Determine MIME-type based on filename 87 return guessContentTypeFromName(uri.toString()); 88 } 89 90 91 @Override openAssetFile(Uri uri, String mode)92 public AssetFileDescriptor openAssetFile (Uri uri, String mode) 93 throws FileNotFoundException, SecurityException { 94 // ContentProvider interface for opening a file descriptor by URI. This content provider 95 // maps all URIs to the contents of the APK's assets folder, so a file handle to 96 // /res/assets/{uri.path} will be returned. 97 98 // Security check. This content provider only supports read-only access. (Also, the contents 99 // of an APKs assets folder are immutable, so read-write access doesn't make sense here.) 100 if (!"r".equals(mode)) { 101 throw new SecurityException("Only read-only access is supported, mode must be [r]"); 102 } 103 104 // Open asset from within APK and return file descriptor 105 String path = uri.getLastPathSegment(); 106 try { 107 return mAssets.openFd(path); 108 } catch (IOException e) { 109 throw new FileNotFoundException(); 110 } 111 } 112 113 /** 114 * Check if file exists inside APK assets. 115 * 116 * @param path Fully qualified path to file. 117 * @return true if exists, false otherwise. 118 */ fileExists(String path)119 private boolean fileExists(String path) { 120 try { 121 // Check to see if file can be opened. If so, file exists. 122 mAssets.openFd(path).close(); 123 return true; 124 } catch (IOException e) { 125 // Unable to open file descriptor for specified path; file doesn't exist. 126 return false; 127 } 128 } 129 130 @Override query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)131 public Cursor query(Uri uri, String[] projection, String selection, 132 String[] selectionArgs, String sortOrder) { 133 String path = uri.getLastPathSegment(); 134 if (!fileExists(path)) { 135 Log.e(TAG, "Requested file doesn't exist at " + path); 136 return null; 137 } 138 139 // Create matrix cursor 140 if (projection == null) { 141 projection = new String[]{ 142 OpenableColumns.DISPLAY_NAME, 143 OpenableColumns.SIZE, 144 }; 145 } 146 147 MatrixCursor matrixCursor = new MatrixCursor(projection, 1); 148 Object[] row = new Object[projection.length]; 149 for (int col = 0; col < projection.length; col++) { 150 if (OpenableColumns.DISPLAY_NAME.equals(projection[col])) { 151 row[col] = path; 152 } else if (OpenableColumns.SIZE.equals(projection[col])) { 153 try { 154 AssetFileDescriptor afd = openAssetFile(uri, "r"); 155 if (afd != null) { 156 row[col] = Long.valueOf(afd.getLength()); 157 } 158 afd.close(); 159 } catch (IOException e) { 160 Log.e(TAG, e.toString()); 161 } 162 } 163 } 164 matrixCursor.addRow(row); 165 return matrixCursor; 166 } 167 168 // Required/unused ContentProvider methods below. 169 @Override insert(Uri uri, ContentValues contentValues)170 public Uri insert(Uri uri, ContentValues contentValues) { 171 throw new RuntimeException("Operation not supported"); 172 } 173 174 @Override delete(Uri uri, String selection, String[] selectionArgs)175 public int delete(Uri uri, String selection, String[] selectionArgs) { 176 throw new RuntimeException("Operation not supported"); 177 } 178 179 @Override update(Uri uri, ContentValues values, String selection, String[] selectionArgs)180 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { 181 throw new RuntimeException("Operation not supported"); 182 } 183 } 184