/*******************************************************************************
* Copyright 2012-present Pixate, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.pixate.freestyle.util;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLDecoder;
import java.util.regex.Pattern;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
import android.util.Base64;
import android.util.Base64InputStream;
import com.pixate.freestyle.PixateFreestyle;
public class UrlStreamOpener {
// URL schemes
public static final String FILE_SCHEME = "file://";
public static final String DOCUMENTS_SCHEME = "documents://";
public static final String BUNDLE_SCHEME = "bundle://";
public static final String TMP_SCHEME = "tmp://";
public static final String DATA_SCHEME = "data:";
private static final Pattern WHITESPACE = Pattern.compile("\\s+");
private static final String[] RESOURCE_TYPES = { "drawable", "raw" };
private static final String TAG = UrlStreamOpener.class.getSimpleName();
public static InputStream open(Uri uri) {
return open(uri.toString());
}
public static InputStream open(String uri) {
if (uri.startsWith(FILE_SCHEME)) {
// Be sure it's absolute (file:/// instead of file://)
if (uri.startsWith("file:///")) {
return openFileScheme(uri);
} else {
return openFileScheme(uri.replace("file:", "file:/"));
}
} else if (uri.startsWith(DOCUMENTS_SCHEME)) {
return openDocumentScheme(uri);
} else if (uri.startsWith(BUNDLE_SCHEME)) {
return openBundleScheme(uri);
} else if (uri.startsWith(TMP_SCHEME)) {
return openTempScheme(uri);
} else if (uri.startsWith(DATA_SCHEME)) {
return openDataScheme(uri);
} else {
return openImplicit(uri);
}
}
private static InputStream openFileScheme(String urlString) {
try {
URI uri = URI.create(urlString);
return new FileInputStream(new File(uri));
} catch (FileNotFoundException e) {
PXLog.e(TAG, e, "Unable to load file at this url: " + urlString);
return null;
}
}
private static InputStream openDocumentScheme(String urlString) {
return openDocumentScheme(urlString, false);
}
private static InputStream openDocumentScheme(String urlString, boolean suppressErrorLog) {
try {
return PixateFreestyle.getAppContext().openFileInput(
urlString.substring(DOCUMENTS_SCHEME.length()));
} catch (Exception e) {
if (!suppressErrorLog) {
PXLog.e(TAG, e, "Unable to load the document at this url: " + urlString);
}
return null;
}
}
private static InputStream openBundleScheme(String urlString) {
return openBundleScheme(urlString, false);
}
private static InputStream openBundleScheme(String urlString, boolean suppressErrorLog) {
// bundle:// -- can be either a resource
// or an asset.
InputStream stream = null;
Context context = PixateFreestyle.getAppContext();
Resources resources = context.getResources();
String resourceName = urlString.substring(BUNDLE_SCHEME.length());
String base = resourceName;
int lastDot = resourceName.lastIndexOf(".");
if (lastDot >= 0) {
base = resourceName.substring(0, lastDot);
}
int resId = findResourceId(resources, context.getPackageName(), base);
if (resId != 0) {
try {
stream = resources.openRawResource(resId);
} catch (Resources.NotFoundException e) {
// Don't suppress this error since it's remarkable
// that a resource cannot be opened even when we know it
// exists (because we have a non-zero ID for it.)
PXLog.e(TAG, e, "Unable to open asset/resource " + resourceName);
stream = null;
}
} else {
// Was not a resource, maybe an asset.
AssetManager assets = resources.getAssets();
try {
stream = assets.open(resourceName);
} catch (IOException e) {
if (!suppressErrorLog) {
PXLog.e(TAG, e, "Unable to open asset/resource " + resourceName);
}
stream = null;
}
}
return stream;
}
private static InputStream openTempScheme(String urlString) {
Context context = PixateFreestyle.getAppContext();
String fileName = urlString.substring(TMP_SCHEME.length());
try {
return new FileInputStream(context.getCacheDir().getAbsolutePath() + File.separator
+ fileName);
} catch (FileNotFoundException e) {
PXLog.e(TAG, e, "Unable to open temp file " + fileName);
return null;
}
}
private static InputStream openDataScheme(String urlString) {
String cleanString = WHITESPACE.matcher(urlString).replaceAll("");
return openDataUriStream(cleanString);
}
private static InputStream openImplicit(String urlString) {
InputStream stream = openDocumentScheme("documents://" + urlString, true);
if (stream == null) {
stream = openBundleScheme("bundle://" + urlString, true);
}
if (stream == null) {
// last try, open as a regular URL.
// Note that this is a networking call, and cannot be made from the
// main thread.
try {
URL url = new URL(urlString);
stream = url.openStream();
} catch (Exception e) {
PXLog.w(TAG, e, "Failed to open '%s' as URL", urlString);
}
}
if (stream == null) {
PXLog.w(TAG, "Neither a document nor a bundle entry with url '%s' could be opened.",
urlString);
}
return stream;
}
private static int findResourceId(Resources resources, String packageName, String resourceName) {
int result = 0;
for (int i = 0; i < RESOURCE_TYPES.length; i++) {
result = resources.getIdentifier(resourceName, RESOURCE_TYPES[i], packageName);
if (result != 0) {
break;
}
}
if (result == 0) {
// Try built-in (system) resources
for (int i = 0; i < RESOURCE_TYPES.length; i++) {
result = Resources.getSystem().getIdentifier(resourceName, RESOURCE_TYPES[i],
"android");
if (result != 0) {
break;
}
}
}
return result;
}
/**
* @see http
* ://svn.apache.org/viewvc/xmlgraphics/commons/trunk/src/java/org/
* apache /xmlgraphics/util/uri/DataURIResolver.java
*/
private static InputStream openDataUriStream(String uri) {
int commaPos = uri.indexOf(',');
if (commaPos < 0) {
PXLog.w(TAG, "Data uri is malformed: " + uri);
return null;
}
String header = uri.substring(0, commaPos);
String data = uri.substring(commaPos + 1);
if (header.endsWith(";base64")) {
byte[] bytes = data.getBytes();
ByteArrayInputStream encodedStream = new ByteArrayInputStream(bytes);
return new Base64InputStream(encodedStream, Base64.DEFAULT);
} else {
String encoding = "UTF-8";
final int charsetpos = header.indexOf(";charset=");
if (charsetpos > 0) {
encoding = header.substring(charsetpos + 9);
}
try {
return new ByteArrayInputStream(URLDecoder.decode(data, encoding)
.getBytes(encoding));
} catch (Exception e) {
PXLog.e(TAG, e, "Unable to decode data uri contents: " + uri);
}
}
return null;
}
}