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.common.assetprovider;
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.net.Uri;
26 
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 
30 import static java.net.URLConnection.guessContentTypeFromName;
31 
32 /**
33  * Generic content provider, which makes any files available in this app's "assets" directory
34  * available publicly.
35  *
36  * <p>To use, add the following to your AndroidManifest.xml:
37  *
38  * <code><pre>
39  * <provider
40  *   android:name=".AssetProvider"
41  *   android:authorities="[YOUR CONTENT PROVIDER DOMAIN HERE]"
42  *   android:exported="true"/>
43  * </pre></code>
44  */
45 public class AssetProvider extends ContentProvider {
46     AssetManager mAssets;
47 
48     @Override
onCreate()49     public boolean onCreate() {
50         Context ctx = getContext();
51         if (ctx == null) {
52             // Context not available. Give up.
53             return false;
54         }
55         mAssets = ctx.getAssets();
56         return true;
57     }
58 
59     @Override
getType(Uri uri)60     public String getType(Uri uri){
61         // Returns the MIME type for the selected URI, in conformance with the ContentProvider
62         // interface. Looks up the file indicated by /res/assets/{uri.path}, and returns the MIME
63         // type for that file as guessed by the URLConnection class.
64 
65         // Setup
66         String path = uri.getPath();
67 
68         // Check if file exists
69         if (!fileExists(path)) {
70             return null;
71         }
72 
73         // Determine MIME-type based on filename
74         return guessContentTypeFromName(uri.toString());
75     }
76 
77 
78     @Override
openAssetFile(Uri uri, String mode)79     public AssetFileDescriptor openAssetFile (Uri uri, String mode)
80             throws FileNotFoundException, SecurityException {
81         // ContentProvider interface for opening a file descriptor by URI. This content provider
82         // maps all URIs to the contents of the APK's assets folder, so a file handle to
83         // /res/assets/{uri.path} will be returned.
84 
85         // Security check. This content provider only supports read-only access. (Also, the contents
86         // of an APKs assets folder are immutable, so read-write access doesn't make sense here.)
87         if (!"r".equals(mode)) {
88             throw new SecurityException("Only read-only access is supported, mode must be [r]");
89         }
90 
91         // Open asset from within APK and return file descriptor
92         String path = uri.getPath();
93         try {
94             return mAssets.openFd(path);
95         } catch (IOException e) {
96             throw new FileNotFoundException();
97         }
98     }
99 
100     /**
101      * Check if file exists inside APK assets.
102      *
103      * @param path Fully qualified path to file.
104      * @return true if exists, false otherwise.
105      */
fileExists(String path)106     private boolean fileExists(String path) {
107         try {
108             // Check to see if file can be opened. If so, file exists.
109             mAssets.openFd(path).close();
110             return true;
111         } catch (IOException e) {
112             // Unable to open file descriptor for specified path; file doesn't exist.
113             return false;
114         }
115     }
116 
117     // Required/unused ContentProvider methods below.
118     @Override
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)119     public Cursor query(Uri uri, String[] projection, String selection,
120                         String[] selectionArgs, String sortOrder) {
121         // Note: It might be worth implementing support for querying
122         //       android.provider.OpenableColumns here in the future.
123         throw new RuntimeException("Operation not supported");
124     }
125 
126     @Override
insert(Uri uri, ContentValues contentValues)127     public Uri insert(Uri uri, ContentValues contentValues) {
128         throw new RuntimeException("Operation not supported");
129     }
130 
131     @Override
delete(Uri uri, String selection, String[] selectionArgs)132     public int delete(Uri uri, String selection, String[] selectionArgs) {
133         throw new RuntimeException("Operation not supported");
134     }
135 
136     @Override
update(Uri uri, ContentValues values, String selection, String[] selectionArgs)137     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
138         throw new RuntimeException("Operation not supported");
139     }
140 }
141