Commit fcc37452 authored by Filipe de Lima Brito's avatar Filipe de Lima Brito

Add androidsvg library as a module to be able to get last improvements.

parent a42cba32
......@@ -49,6 +49,7 @@ dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation project(':player')
implementation project(':svg')
implementation libraries.kotlin
implementation libraries.coroutines
......@@ -92,10 +93,6 @@ dependencies {
kapt libraries.kotshiCompiler
implementation libraries.kotshiApi
implementation libraries.floatingSearchView
implementation libraries.androidSvg
implementation libraries.aVLoadingIndicatorView
implementation libraries.textDrawable
......
......@@ -23,8 +23,8 @@ import com.facebook.imagepipeline.image.QualityInfo
object SvgDecoder {
val svgFormat = ImageFormat("SVG_FORMAT", "svg")
// We do not include the closing ">" since there can be additional information.
private val headerTag = "<?xml"
private val possibleHeaderTags = arrayOf(ImageFormatCheckerUtils.asciiBytes("<svg"))
private val headerTag = "<svg"
private val possibleHeaderTags = arrayOf(ImageFormatCheckerUtils.asciiBytes("<?xml"))
/**
* Custom SVG format checker that verifies that the header of the file corresponds to our [SvgDecoder.headerTag] or [SvgDecoder.possibleHeaderTags].
......
......@@ -13,7 +13,7 @@ buildscript {
classpath "com.android.tools.build:gradle:3.0.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:${versions.dokka}"
classpath 'com.google.gms:google-services:3.1.2'
classpath 'com.google.gms:google-services:3.2.0'
classpath 'io.fabric.tools:gradle:1.+'
// NOTE: Do not place your application dependencies here; they belong
......
......@@ -24,8 +24,6 @@ ext {
fresco : '1.7.1',
frescoImageViewer : '0.5.0',
kotshi : '0.3.0',
floatingSearchView : '2.1.1',
androidSvg : '1.2.1',
aVLoadingIndicatorView : '2.1.3',
textDrawable : '1.0.2',
......@@ -81,10 +79,6 @@ ext {
kotshiApi : "se.ansman.kotshi:api:${versions.kotshi}",
kotshiCompiler : "se.ansman.kotshi:compiler:${versions.kotshi}",
floatingSearchView : "com.github.arimorty:floatingsearchview:${versions.floatingSearchView}",
androidSvg : "com.caverock:androidsvg:${versions.androidSvg}",
aVLoadingIndicatorView : "com.wang.avi:library:${versions.aVLoadingIndicatorView}",
textDrawable : "com.github.rocketchat:textdrawable:${versions.textDrawable}",
......
include ':app', ':player'
\ No newline at end of file
include ':app', ':player', ':svg'
\ No newline at end of file
apply plugin: 'com.android.library'
android {
compileSdkVersion versions.compileSdk
defaultConfig {
minSdkVersion 21
targetSdkVersion versions.targetSdk
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.caverock.androidsvg"
android:versionCode="1"
android:versionName="1.2.3-beta-1">
<uses-sdk/>
</manifest>
\ No newline at end of file
This diff is collapsed.
/*
Copyright 2014 Paul LeBeau, Cave Rock Software Ltd.
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.caverock.androidsvg;
/**
* Parse a SVG/CSS 'integer' or hex number from a String.
*
* We use our own parser to gain a bit of speed. This routine is
* around twice as fast as the system one.
*/
class IntegerParser
{
private int pos;
private long value;
private IntegerParser(long value, int pos)
{
this.value = value;
this.pos = pos;
}
/*
* Return the value of pos after the parse.
*/
int getEndPos()
{
return this.pos;
}
/*
* Scan the string for an SVG integer.
* Assumes maxPos will not be greater than input.length().
*/
/*
private static IntegerParser parseInt(String input, int startpos, int len)
{
int pos = startpos;
boolean isNegative = false;
long value = 0;
if (pos >= len)
return null; // String is empty - no number found
char ch = input.charAt(pos);
switch (ch) {
case '-': isNegative = true;
// fall through
case '+': pos++;
}
int sigStart = pos;
while (pos < len)
{
ch = input.charAt(pos);
if (ch >= '0' && ch <= '9')
{
if (isNegative) {
value = value * 10 - ((int)ch - (int)'0');
if (value < Integer.MIN_VALUE)
return null;
} else {
value = value * 10 + ((int)ch - (int)'0');
if (value > Integer.MAX_VALUE)
return null;
}
}
else
break;
pos++;
}
// Have we seen anything number-ish at all so far?
if (pos == sigStart) {
return null;
}
return new IntegerParser(isNegative, value, pos);
}
*/
/*
* Return the parsed value as an actual float.
*/
public int value()
{
return (int)value;
}
/*
* Scan the string for an SVG hex integer.
* Assumes maxPos will not be greater than input.length().
*/
static IntegerParser parseHex(String input, int startpos, int len)
{
int pos = startpos;
long value = 0;
char ch;
if (pos >= len)
return null; // String is empty - no number found
while (pos < len)
{
ch = input.charAt(pos);
if (ch >= '0' && ch <= '9')
{
value = value * 16 + ((int)ch - (int)'0');
}
else if (ch >= 'A' && ch <= 'F')
{
value = value * 16 + ((int)ch - (int)'A') + 10;
}
else if (ch >= 'a' && ch <= 'f')
{
value = value * 16 + ((int)ch - (int)'a') + 10;
}
else
break;
if (value > 0xffffffffL)
return null;
pos++;
}
// Have we seen anything number-ish at all so far?
if (pos == startpos) {
return null;
}
return new IntegerParser(value, pos);
}
}
/*
Copyright 2013 Paul LeBeau, Cave Rock Software Ltd.
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.caverock.androidsvg;
/**
* Configure debugging on or off.
*/
class LibConfig
{
static final boolean DEBUG = false;
}
/*
Copyright 2014 Paul LeBeau, Cave Rock Software Ltd.
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.caverock.androidsvg;
/**
* Parse a SVG 'number' or a CSS 'number' from a String.
*
* We use our own parser because the one in Android (from Harmony I think) is slow.
*
* An SVG 'number' is defined as
* integer ([Ee] integer)?
* | [+-]? [0-9]* "." [0-9]+ ([Ee] integer)?
* Where 'integer' is
* [+-]? [0-9]+
* CSS numbers were different, but have now been updated to a compatible definition (see 2.1 Errata)
* [+-]?([0-9]+|[0-9]*\.[0-9]+)(e[+-]?[0-9]+)?
*
*/
class NumberParser
{
private int pos;
/*
* Return the value of pos after the parse.
*/
int getEndPos()
{
return this.pos;
}
/*
* Scan the string for an SVG number.
* Assumes maxPos will not be greater than str.length().
*/
float parseNumber(String input, int startpos, int len)
{
boolean isNegative = false;
long significand = 0;
int numDigits = 0;
int numLeadingZeroes = 0;
int numTrailingZeroes = 0;
boolean decimalSeen = false;
int sigStart;
int decimalPos = 0;
int exponent;
long TOO_BIG = Long.MAX_VALUE / 10;
pos = startpos;
if (pos >= len)
return Float.NaN; // String is empty - no number found
char ch = input.charAt(pos);
switch (ch) {
case '-': isNegative = true;
// fall through
case '+': pos++;
}
sigStart = pos;
while (pos < len)
{
ch = input.charAt(pos);
if (ch == '0')
{
if (numDigits == 0) {
numLeadingZeroes++;
} else {
// We potentially skip trailing zeroes. Keep count for now.
numTrailingZeroes++;
}
}
else if (ch >= '1' && ch <= '9')
{
// Multiply any skipped zeroes into buffer
numDigits += numTrailingZeroes;
while (numTrailingZeroes > 0) {
if (significand > TOO_BIG) {
//Log.e("Number is too large");
return Float.NaN;
}
significand *= 10;
numTrailingZeroes--;
}
if (significand > TOO_BIG) {
// We will overflow if we continue...
//Log.e("Number is too large");
return Float.NaN;
}
significand = significand * 10 + ((int)ch - (int)'0');
numDigits++;
if (significand < 0)
return Float.NaN; // overflowed from +ve to -ve
}
else if (ch == '.')
{
if (decimalSeen) {
// Stop parsing here. We may be looking at a new number.
break;
}
decimalPos = pos - sigStart;
decimalSeen = true;
}
else
break;
pos++;
}
if (decimalSeen && pos == (decimalPos + 1)) {
// No digits following decimal point (eg. "1.")
//Log.e("Missing fraction part of number");
return Float.NaN;
}
// Have we seen anything number-ish at all so far?
if (numDigits == 0) {
if (numLeadingZeroes == 0) {
//Log.e("Number not found");
return Float.NaN;
}
// Leading zeroes have been seen though, so we
// treat that as a '0'.
numDigits = 1;
}
if (decimalSeen) {
exponent = decimalPos - numLeadingZeroes - numDigits;
} else {
exponent = numTrailingZeroes;
}
// Now look for exponent
if (pos < len)
{
ch = input.charAt(pos);
if (ch == 'E' || ch == 'e')
{
boolean expIsNegative = false;
int expVal = 0;
boolean abortExponent = false;
pos++;
if (pos == len) {
// Incomplete exponent.
//Log.e("Incomplete exponent of number");
return Float.NaN;
}
switch (input.charAt(pos)) {
case '-': expIsNegative = true;
// fall through
case '+': pos++;
break;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
break; // acceptable next char
default:
// any other character is a failure, ie no exponent.
// Could be something legal like "em" though.
abortExponent = true;
pos--; // reset pos to position of 'E'/'e'
}
if (!abortExponent)
{
int expStart = pos;
while (pos < len)
{
ch = input.charAt(pos);
if (ch >= '0' && ch <= '9')
{
if (expVal > TOO_BIG) {
// We will overflow if we continue...
//Log.e("Exponent of number is too large");
return Float.NaN;
}
expVal = expVal * 10 + ((int)ch - (int)'0');
pos++;
}
else
break;
}
// Check that at least some exponent digits were read
if (pos == expStart) {
//Log.e(""Incomplete exponent of number"");
return Float.NaN;
}
if (expIsNegative)
exponent -= expVal;
else
exponent += expVal;
}
}
}
// Quick check to eliminate huge exponents.
// Biggest float is (2 - 2^23) . 2^127 ~== 3.4e38
// Biggest negative float is 2^-149 ~== 1.4e-45
// Some numbers that will overflow will get through the scan
// and be returned as 'valid', yet fail when value() is called.
// However they will be very rare and not worth slowing down
// the parse for.
if ((exponent + numDigits) > 39 || (exponent + numDigits) < -44)
return Float.NaN;
float f = (float) significand;
if (significand != 0)
{
// Do exponents > 0
if (exponent > 0)
{
f *= positivePowersOf10[exponent];
}
else if (exponent < 0)
{
// Some valid numbers can have an exponent greater than the max (ie. < -38)
// for a float. For example, significand=123, exponent=-40
// If that's the case, we need to apply the exponent in two steps.
if (exponent < -38) {
// Long.MAX_VALUE is 19 digits, so taking 20 off the exponent should be enough.
f *= 1e-20;
exponent += 20;
}
// Do exponents < 0
f *= negativePowersOf10[-exponent];
}
}
return (isNegative) ? -f : f;
}
private static final float positivePowersOf10[] = {
1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f, 1e7f, 1e8f, 1e9f,
1e10f, 1e11f, 1e12f, 1e13f, 1e14f, 1e15f, 1e16f, 1e17f, 1e18f, 1e19f,
1e20f, 1e21f, 1e22f, 1e23f, 1e24f, 1e25f, 1e26f, 1e27f, 1e28f, 1e29f,
1e30f, 1e31f, 1e32f, 1e33f, 1e34f, 1e35f, 1e36f, 1e37f, 1e38f
};
private static final float negativePowersOf10[] = {
1e0f, 1e-1f, 1e-2f, 1e-3f, 1e-4f, 1e-5f, 1e-6f, 1e-7f, 1e-8f, 1e-9f,
1e-10f, 1e-11f, 1e-12f, 1e-13f, 1e-14f, 1e-15f, 1e-16f, 1e-17f, 1e-18f, 1e-19f,
1e-20f, 1e-21f, 1e-22f, 1e-23f, 1e-24f, 1e-25f, 1e-26f, 1e-27f, 1e-28f, 1e-29f,
1e-30f, 1e-31f, 1e-32f, 1e-33f, 1e-34f, 1e-35f, 1e-36f, 1e-37f, 1e-38f
};
}
/*
Copyright 2013 Paul LeBeau, Cave Rock Software Ltd.
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.caverock.androidsvg;
/**
* The SVGPositioning class tells the renderer how to scale and position the
* SVG document in the current viewport. It is roughly equivalent to the
* {@code preserveAspectRatio} attribute of an {@code <svg>} element.
* <p>
* In order for scaling to happen, the SVG document must have a viewBox attribute set.
* For example:
*
* <pre>
* {@code
* <svg version="1.1" viewBox="0 0 200 100">
* }
* </pre>
*
*/
public class PreserveAspectRatio
{
private Alignment alignment;
private Scale scale;
/**
* Draw doucment at its natural position and scale.
*/
@SuppressWarnings("unused")
public static final PreserveAspectRatio UNSCALED = new PreserveAspectRatio(null, null);
/**
* Stretch horizontally and vertically to fill the viewport.
*/
@SuppressWarnings("WeakerAccess")
public static final PreserveAspectRatio STRETCH = new PreserveAspectRatio(Alignment.None, null);
/**
* Keep the document's aspect ratio, but scale it so that it fits neatly inside the viewport.
* <p>
* The document will be centred in the viewport and may have blank strips at either the top and
* bottom of the viewport or at the sides.
*/
@SuppressWarnings("WeakerAccess")
public static final PreserveAspectRatio LETTERBOX = new PreserveAspectRatio(Alignment.XMidYMid, Scale.Meet);
/**
* Keep the document's aspect ratio, but scale it so that it fits neatly inside the viewport.
* <p>
* The document will be positioned at the top of tall and narrow viewports, and at the left of short
* and wide viewports.
*/
@SuppressWarnings("unused")
public static final PreserveAspectRatio START = new PreserveAspectRatio(Alignment.XMinYMin, Scale.Meet);
/**
* Keep the document's aspect ratio, but scale it so that it fits neatly inside the viewport.
* <p>
* The document will be positioned at the bottom of tall and narrow viewports, and at the right of short
* and wide viewports.
*/
@SuppressWarnings("unused")
public static final PreserveAspectRatio END = new PreserveAspectRatio(Alignment.XMaxYMax, Scale.Meet);
/**
* Keep the document's aspect ratio, but scale it so that it fits neatly inside the viewport.
* <p>
* The document will be positioned at the top of tall and narrow viewports, and at the centre of
* short and wide viewports.
*/
@SuppressWarnings("unused")
public static final PreserveAspectRatio TOP = new PreserveAspectRatio(Alignment.XMidYMin, Scale.Meet);
/**
* Keep the document's aspect ratio, but scale it so that it fits neatly inside the viewport.
* <p>
* The document will be positioned at the bottom of tall and narrow viewports, and at the centre of
* short and wide viewports.
*/
@SuppressWarnings("unused")
public static final PreserveAspectRatio BOTTOM = new PreserveAspectRatio(Alignment.XMidYMax, Scale.Meet);
/**
* Keep the document's aspect ratio, but scale it so that it fills the entire viewport.
* This may result in some of the document falling outside the viewport.
* <p>
* The document will be positioned so that the centre of the document will always be visible,
* but the edges of the document may not.
*/
@SuppressWarnings("unused")
public static final PreserveAspectRatio FULLSCREEN = new PreserveAspectRatio(Alignment.XMidYMid, Scale.Slice);
/**
* Keep the document's aspect ratio, but scale it so that it fills the entire viewport.
* This may result in some of the document falling outside the viewport.
* <p>
* The document will be positioned so that the top left of the document will always be visible,
* but the right hand or bottom edge may not.
*/
@SuppressWarnings("unused")
public static final PreserveAspectRatio FULLSCREEN_START = new PreserveAspectRatio(Alignment.XMinYMin, Scale.Slice);
/**
* Determines how the document is to me positioned relative to the viewport (normally the canvas).
* <p>
* For the value {@code none}, the document is stretched to fit the viewport dimensions. For all
* other values, the aspect ratio of the document is kept the same but the document is scaled to
* fit the viewport.
*/
public enum Alignment
{
/** Document is stretched to fit both the width and height of the viewport. When using this Alignment value, the value of Scale is not used and will be ignored. */
None,
/** Document is positioned at the top left of the viewport. */
XMinYMin,
/** Document is positioned at the centre top of the viewport. */
XMidYMin,
/** Document is positioned at the top right of the viewport. */
XMaxYMin,
/** Document is positioned at the middle left of the viewport. */
XMinYMid,
/** Document is centred in the viewport both vertically and horizontally. */
XMidYMid,
/** Document is positioned at the middle right of the viewport. */
XMaxYMid,
/** Document is positioned at the bottom left of the viewport. */
XMinYMax,
/** Document is positioned at the bottom centre of the viewport. */
XMidYMax,
/** Document is positioned at the bottom right of the viewport. */
XMaxYMax
}
/**
* Determine whether the scaled document fills the viewport entirely or is scaled to
* fill the viewport without overflowing.
*/
public enum Scale
{
/**
* The document is scaled so that it is as large as possible without overflowing the viewport.
* There may be blank areas on one or more sides of the document.
*/
Meet,
/**
* The document is scaled so that entirely fills the viewport. That means that some of the
* document may fall outside the viewport and will not be rendered.
*/
Slice
}
PreserveAspectRatio(Alignment alignment, Scale scale)
{
this.alignment = alignment;
this.scale = scale;
}
/**
* Returns the alignment value of this instance.
* @return the alignment
*/
@SuppressWarnings("WeakerAccess")
public Alignment getAlignment()
{
return alignment;
}
/**
* Returns the scale value of this instance.
* @return the scale
*/
@SuppressWarnings("WeakerAccess")
public Scale getScale()
{
return scale;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PreserveAspectRatio other = (PreserveAspectRatio) obj;
return (alignment == other.alignment && scale == other.scale);
}
}
This diff is collapsed.
/*
Copyright 2013 Paul LeBeau, Cave Rock Software Ltd.
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.caverock.androidsvg;
import android.graphics.Bitmap;
import android.graphics.Typeface;
/**
* Resolver class used by the renderer when processing Text and Image elements.
* <p>
* The default behaviour is to tell AndroidSVG that the reference could not be found.
* <p>
* Extend this class and override the methods if you want to customise how AndroidSVG treats font and image references.
*/
public abstract class SVGExternalFileResolver
{
/**
* Called by renderer to resolve font references in &lt;text&gt; elements.
* <p>
* Return a {@code Typeface} instance, or null if you want the renderer to ignore
* this font and use the default Android font instead.
* <p>
* Note that AndroidSVG does not attempt to cache Typeface references. If you want
* them cached, for speed or memory reasons, you should do so yourself.
*
* @param fontFamily Font family as specified in a font-family style attribute.
* @param fontWeight Font weight as specified in a font-weight style attribute.
* @param fontStyle Font style as specified in a font-style style attribute.
* @return an Android Typeface instance, or null
*/
public Typeface resolveFont(String fontFamily, int fontWeight, String fontStyle)
{
return null;
}
/**
* Called by renderer to resolve image file references in &lt;image&gt; elements.
* <p>
* Return a {@code Bitmap} instance, or null if you want the renderer to ignore
* this image.
* <p>
* Note that AndroidSVG does not attempt to cache Bitmap references. If you want
* them cached, for speed or memory reasons, you should do so yourself.
*
* @param filename the filename as provided in the xlink:href attribute of a &lt;image&gt; element.
* @return an Android Bitmap object, or null if the image could not be found.
*/
public Bitmap resolveImage(String filename)
{
return null;
}
/**
* Called by renderer to determine whether a particular format is supported. In particular,
* this method is used in &lt;switch&gt; elements when processing {@code requiredFormats}
* conditionals.
*
* @param mimeType A MIME type (such as "image/jpeg").
* @return true if your {@code resolveImage()} implementation supports this file format.
*/
public boolean isFormatSupported(String mimeType)
{
return false;
}
}
/*
Copyright 2013 Paul LeBeau, Cave Rock Software Ltd.
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.caverock.androidsvg;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.drawable.PictureDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
/**
* SVGImageView is a View widget that allows users to include SVG images in their layouts.
*
* It is implemented as a thin layer over {@code android.widget.ImageView}.
* <p>
* In its present form it has one significant limitation. It uses the {@link SVG#renderToPicture()}
* method. That means that SVG documents that use {@code <mask>} elements will not display correctly.
*
* @attr ref R.styleable#SVGImageView_svg
*/
@SuppressWarnings("JavaDoc")
public class SVGImageView extends ImageView
{
private static Method setLayerTypeMethod = null;
static {
try
{
setLayerTypeMethod = View.class.getMethod("setLayerType", Integer.TYPE, Paint.class);
}
catch (NoSuchMethodException e) { /* do nothing */ }
}
public SVGImageView(Context context)
{
super(context);
}
public SVGImageView(Context context, AttributeSet attrs)
{
super(context, attrs, 0);
init(attrs, 0);
}
public SVGImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(attrs, defStyle);
}
private void init(AttributeSet attrs, int defStyle)
{
if (isInEditMode())
return;
TypedArray a = getContext().getTheme()
.obtainStyledAttributes(attrs, R.styleable.SVGImageView, defStyle, 0);
try
{
int resourceId = a.getResourceId(R.styleable.SVGImageView_svg, -1);
if (resourceId != -1) {
setImageResource(resourceId);
return;
}
String url = a.getString(R.styleable.SVGImageView_svg);
if (url != null)
{
Uri uri = Uri.parse(url);
if (internalSetImageURI(uri, false))
return;
// Last chance, try loading it as an asset filename
setImageAsset(url);
}
} finally {
a.recycle();
}
}
/**
* Directly set the SVG.
*/
public void setSVG(SVG mysvg)
{
if (mysvg == null)
throw new IllegalArgumentException("Null value passed to setSVG()");
setSoftwareLayerType();
setImageDrawable(new PictureDrawable(mysvg.renderToPicture()));
}
/**
* Load an SVG image from the given resource id.
*/
@Override
public void setImageResource(int resourceId)
{
new LoadResourceTask(getContext(), resourceId).execute();
}
/**
* Load an SVG image from the given resource URI.
*/
@Override
public void setImageURI(Uri uri)
{
internalSetImageURI(uri, true);
}
/**
* Load an SVG image from the given asset filename.
*/
public void setImageAsset(String filename)
{
new LoadAssetTask(getContext(), filename).execute();
}
/*
* Attempt to set a picture from a Uri. Return true if it worked.
*/
private boolean internalSetImageURI(Uri uri, boolean isDirectRequestFromUser)
{
InputStream is;
try
{
is = getContext().getContentResolver().openInputStream(uri);
}
catch (FileNotFoundException e)
{
if (isDirectRequestFromUser)
Log.e("SVGImageView", "File not found: " + uri);
return false;
}
new LoadURITask().execute(is);
return true;
}
//===============================================================================================
private class LoadResourceTask extends AsyncTask<Integer, Integer, Picture>
{
private Context context;
private int resourceId;
LoadResourceTask(Context context, int resourceId)
{
this.context = context;
this.resourceId = resourceId;
}
protected Picture doInBackground(Integer... params)
{
try
{
SVG svg = SVG.getFromResource(context, resourceId);
return svg.renderToPicture();
}
catch (SVGParseException e)
{
Log.e("SVGImageView", String.format("Error loading resource 0x%x: %s", resourceId, e.getMessage()));
}
return null;
}
protected void onPostExecute(Picture picture)
{
if (picture != null) {
setSoftwareLayerType();
setImageDrawable(new PictureDrawable(picture));
}
}
}
private class LoadAssetTask extends AsyncTask<String, Integer, Picture>
{
private Context context;
private String filename;
LoadAssetTask(Context context, String filename)
{
this.context = context;
this.filename = filename;
}
protected Picture doInBackground(String... params)
{
try
{
SVG svg = SVG.getFromAsset(context.getAssets(), filename);
return svg.renderToPicture();
}
catch (SVGParseException e)
{
Log.e("SVGImageView", "Error loading file " + filename + ": " + e.getMessage());
}
catch (FileNotFoundException e)
{
Log.e("SVGImageView", "File not found: " + filename);
}
catch (IOException e)
{
Log.e("SVGImageView", "Unable to load asset file: " + filename, e);
}
return null;
}
protected void onPostExecute(Picture picture)
{
if (picture != null) {
setSoftwareLayerType();
setImageDrawable(new PictureDrawable(picture));
}
}
}
private class LoadURITask extends AsyncTask<InputStream, Integer, Picture>
{
protected Picture doInBackground(InputStream... is)
{
try
{
SVG svg = SVG.getFromInputStream(is[0]);
return svg.renderToPicture();
}
catch (SVGParseException e)
{
Log.e("SVGImageView", "Parse error loading URI: " + e.getMessage());
}
finally
{
try
{
is[0].close();
}
catch (IOException e) { /* do nothing */ }
}
return null;
}
protected void onPostExecute(Picture picture)
{
if (picture != null) {
setSoftwareLayerType();
setImageDrawable(new PictureDrawable(picture));
}
}
}
//===============================================================================================
/*
* Use reflection to call an API 11 method from this library (which is configured with a minSdkVersion of 8)
*/
private void setSoftwareLayerType()
{
if (setLayerTypeMethod == null)
return;
try
{
int LAYER_TYPE_SOFTWARE = View.class.getField("LAYER_TYPE_SOFTWARE").getInt(new View(getContext()));
setLayerTypeMethod.invoke(this, LAYER_TYPE_SOFTWARE, null);
}
catch (Exception e)
{
Log.w("SVGImageView", "Unexpected failure calling setLayerType", e);
}
}
}
/*
Copyright 2013 Paul LeBeau, Cave Rock Software Ltd.
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.caverock.androidsvg;
import org.xml.sax.SAXException;
/**
* Thrown by the parser if a problem is found in the SVG file.
* Extends SAXException rather than Exception to avoid unnecessary casts in the SAX parser handling code.
*/
public class SVGParseException extends SAXException
{
SVGParseException(String msg)
{
super(msg);
}
SVGParseException(String msg, Exception cause)
{
super(msg, cause);
}
}
This diff is collapsed.
/*
Copyright 2013 Paul LeBeau, Cave Rock Software Ltd.
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.caverock.androidsvg;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Set;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.util.Log;
/**
* A sample implementation of {@link SVGExternalFileResolver} that retrieves files from
* an application's "assets" folder.
*/
public class SimpleAssetResolver extends SVGExternalFileResolver
{
private static final String TAG = SimpleAssetResolver.class.getSimpleName();
private AssetManager assetManager;
public SimpleAssetResolver(AssetManager assetManager)
{
super();
this.assetManager = assetManager;
}
private static final Set<String> supportedFormats = new HashSet<>(8);
// Static initialiser
static {
// PNG, JPEG and SVG are required by the SVG 1.2 spec
supportedFormats.add("image/svg+xml");
supportedFormats.add("image/jpeg");
supportedFormats.add("image/png");
// Other image formats supported by Android BitmapFactory
supportedFormats.add("image/pjpeg");
supportedFormats.add("image/gif");
supportedFormats.add("image/bmp");
supportedFormats.add("image/x-windows-bmp");
// .webp supported in 4.0+ (ICE_CREAM_SANDWICH)
if (android.os.Build.VERSION.SDK_INT >= 14) {
supportedFormats.add("image/webp");
}
}
/**
* Attempt to find the specified font in the "assets" folder and return a Typeface object.
* For the font name "Foo", first the file "Foo.ttf" will be tried and if that fails, "Foo.otf".
*/
@Override
public Typeface resolveFont(String fontFamily, int fontWeight, String fontStyle)
{
Log.i(TAG, "resolveFont("+fontFamily+","+fontWeight+","+fontStyle+")");
// Try font name with suffix ".ttf"
try
{
return Typeface.createFromAsset(assetManager, fontFamily + ".ttf");
}
catch (RuntimeException ignored) {}
// That failed, so try ".otf"
try
{
return Typeface.createFromAsset(assetManager, fontFamily + ".otf");
}
catch (RuntimeException e)
{
return null;
}
}
/**
* Attempt to find the specified image file in the "assets" folder and return a decoded Bitmap.
*/
@Override
public Bitmap resolveImage(String filename)
{
Log.i(TAG, "resolveImage("+filename+")");
try
{
InputStream istream = assetManager.open(filename);
return BitmapFactory.decodeStream(istream);
}
catch (IOException e1)
{
return null;
}
}
/**
* Returns true when passed the MIME types for SVG, JPEG, PNG or any of the
* other bitmap image formats supported by Android's BitmapFactory class.
*/
@Override
public boolean isFormatSupported(String mimeType)
{
return supportedFormats.contains(mimeType);
}
}
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SVGImageView">
<!-- Location of the SVG document. -->
<attr name="svg" format="reference|string"/>
</declare-styleable>
</resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment