From bb268b10d33f721b406ca37e0efce2490c9513e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Evelio=20Tarazona=20C=C3=A1ceres?= Date: Mon, 22 Dec 2014 18:16:31 -0500 Subject: [PATCH] Initial commit :octocat: --- .gitignore | 129 ++ LICENSE | 20 + README.md | 114 ++ build.gradle | 22 + demo/build.gradle | 27 + demo/proguard-rules.txt | 17 + demo/src/main/AndroidManifest.xml | 22 + .../com/telly/mrvector/demo/BaseActivity.java | 63 + .../mrvector/demo/BasicInflateActivity.java | 60 + .../demo/InflateFromLayoutActivity.java | 50 + .../main/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 5527 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 3416 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 7895 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 12506 bytes .../main/res/drawable-xxxhdpi/ic_launcher.png | Bin 0 -> 16753 bytes demo/src/main/res/drawable/e.xml | 44 + demo/src/main/res/drawable/logo.xml | 34 + .../res/drawable/sample_vector_drawable.xml | 48 + demo/src/main/res/drawable/simple.xml | 38 + demo/src/main/res/drawable/vector_android.xml | 35 + .../res/layout/activity_basic_inflate.xml | 30 + .../layout/activity_inflate_from_layout.xml | 30 + .../src/main/res/menu/layout_inflate_menu.xml | 11 + demo/src/main/res/menu/main_menu.xml | 9 + demo/src/main/res/values-w820dp/dimens.xml | 3 + demo/src/main/res/values/colors.xml | 7 + demo/src/main/res/values/dimens.xml | 7 + demo/src/main/res/values/integers.xml | 7 + demo/src/main/res/values/strings.xml | 10 + demo/src/main/res/values/styles.xml | 8 + extras/logo.sketch | Bin 0 -> 40960 bytes gradle.properties | 25 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 49896 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 ++ gradlew.bat | 90 + library/build.gradle | 26 + library/gradle-mvn-push.gradle | 114 ++ library/proguard-rules.txt | 17 + library/src/main/AndroidManifest.xml | 4 + .../java/android/content/res/MrResources.java | 93 + .../java/android/support/v4/util/IntSet.java | 147 ++ .../java/com/telly/mrvector/MrVector.java | 113 ++ .../java/com/telly/mrvector/PathParser.java | 654 +++++++ .../main/java/com/telly/mrvector/Utils.java | 115 ++ .../com/telly/mrvector/VectorDrawable.java | 1588 +++++++++++++++++ library/src/main/res/values/attrs.xml | 183 ++ settings.gradle | 1 + 48 files changed, 4185 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 build.gradle create mode 100644 demo/build.gradle create mode 100644 demo/proguard-rules.txt create mode 100644 demo/src/main/AndroidManifest.xml create mode 100644 demo/src/main/java/com/telly/mrvector/demo/BaseActivity.java create mode 100644 demo/src/main/java/com/telly/mrvector/demo/BasicInflateActivity.java create mode 100644 demo/src/main/java/com/telly/mrvector/demo/InflateFromLayoutActivity.java create mode 100755 demo/src/main/res/drawable-hdpi/ic_launcher.png create mode 100755 demo/src/main/res/drawable-mdpi/ic_launcher.png create mode 100755 demo/src/main/res/drawable-xhdpi/ic_launcher.png create mode 100755 demo/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100755 demo/src/main/res/drawable-xxxhdpi/ic_launcher.png create mode 100644 demo/src/main/res/drawable/e.xml create mode 100644 demo/src/main/res/drawable/logo.xml create mode 100644 demo/src/main/res/drawable/sample_vector_drawable.xml create mode 100644 demo/src/main/res/drawable/simple.xml create mode 100644 demo/src/main/res/drawable/vector_android.xml create mode 100644 demo/src/main/res/layout/activity_basic_inflate.xml create mode 100644 demo/src/main/res/layout/activity_inflate_from_layout.xml create mode 100644 demo/src/main/res/menu/layout_inflate_menu.xml create mode 100644 demo/src/main/res/menu/main_menu.xml create mode 100644 demo/src/main/res/values-w820dp/dimens.xml create mode 100644 demo/src/main/res/values/colors.xml create mode 100644 demo/src/main/res/values/dimens.xml create mode 100644 demo/src/main/res/values/integers.xml create mode 100644 demo/src/main/res/values/strings.xml create mode 100644 demo/src/main/res/values/styles.xml create mode 100644 extras/logo.sketch create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 library/build.gradle create mode 100644 library/gradle-mvn-push.gradle create mode 100644 library/proguard-rules.txt create mode 100644 library/src/main/AndroidManifest.xml create mode 100644 library/src/main/java/android/content/res/MrResources.java create mode 100644 library/src/main/java/android/support/v4/util/IntSet.java create mode 100644 library/src/main/java/com/telly/mrvector/MrVector.java create mode 100644 library/src/main/java/com/telly/mrvector/PathParser.java create mode 100644 library/src/main/java/com/telly/mrvector/Utils.java create mode 100644 library/src/main/java/com/telly/mrvector/VectorDrawable.java create mode 100644 library/src/main/res/values/attrs.xml create mode 100644 settings.gradle diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..47e4981 --- /dev/null +++ b/.gitignore @@ -0,0 +1,129 @@ +# Created by http://www.gitignore.io + +### Android ### +# Built application files +*.apk +*.ap_ + +# Files for the Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ + +# Gradle files +.gradle/ +build/ +.gradletasknamecache + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +#Log Files +*.log + + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +## Directory-based project format +.idea/ +# if you remove the above rule, at least ignore user-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# and these sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml + +## File-based project format +*.ipr +*.iml +*.iws + +## Additional for IntelliJ +out/ + +# generated by mpeltonen/sbt-idea plugin +.idea_modules/ + +# generated by JIRA plugin +atlassian-ide-plugin.xml + +# generated by Crashlytics plugin (for Android Studio and Intellij) +com_crashlytics_export_strings.xml + + +### Gradle ### +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + + +### Eclipse ### +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + + +### OSX ### +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0db4d32 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright Telly, Inc. and other contributors. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to permit +persons to whom the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8e164f --- /dev/null +++ b/README.md @@ -0,0 +1,114 @@ +Mr. Vector +========== +![Mr. Vector](http://i.imgur.com/ucFr5T7.png) + +AKA VectorDrawableCompat: A 14+ backport of [VectorDrawable](https://developer.android.com/reference/android/graphics/drawable/VectorDrawable.html). + +### Demo + +![Le demo](http://i.imgur.com/nG4uQiN.gif) + +[![Mr. Vector Demo on Google Play Store](http://developer.android.com/images/brand/en_generic_rgb_wo_60.png)](https://play.google.com/store/apps/details?id=com.telly.mrvector.demo) + +### Usage + +See demo, at this point latest version is `0.1.0` + +```groovy +compile 'com.telly:mrvector:(insert latest version)' +``` + +### Basic inflate +```java +Drawable drawable = MrVector.inflate(getResources(), R.drawable.vector_android); +``` + +Unfortunately due some inflate weirdness (able to read some correctly but not others) for now (will fix promise) you'll have to duplicate (sucks I know) all your `android:` attributes, in example: + + +```xml + + + + +``` + +### Inflate from Layout (WIP) + +Use it as a regular drawable: + +```xml + + + android:icon="@drawable/vector_drawable" + +``` + +```xml + + + android:src="@drawable/vector_drawable" + +``` + +And then from your `Application` or `Activity`: + +```java +\\ ... + {{ + MrVector.register( + R.drawable.vector_drawable, + R.drawable.another_vector_drawable, + \\ ... + ); + }} +\\ ... + @Override + protected void attachBaseContext(Context newBase) { + super.attachBaseContext(MrVector.wrap(newBase)); + } +\\ ... +``` + +### Roadmap +Right now only basic inflating works, this is the list of features planed: + +- [ ] Put this in GH issues. +- [ ] Full inflate from layout support (partially implemented except for `TypedArray` calling directly `loadDrawable`, which sadly is key) +- [ ] Get rid of `auto` namespace, use `android` namespace as much as possible (no duplicated attributes). +- [ ] Tint support. +- [ ] Animation support ([AnimatedVectorDrawable](https://developer.android.com/reference/android/graphics/drawable/AnimatedVectorDrawable.html)). + +On the long run, it would be nice to see (but no promises): + +- [ ] Per node animation. +- [ ] Additional SVG support (e.g. using [svg-android](https://code.google.com/p/svg-android/) or [svgandroid](https://code.google.com/p/androidsvg/)). +- [ ] SVG animation support. + +### License & About + +See LICENSE file, logo built from [opoloo/androidicons](https://github.com/opoloo/androidicons). + +From [@eveliotc](https://plus.google.com/u/0/+EvelioTarazonaC%C3%A1ceres/posts) @ [Telly](https://telly.com/) \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..7c69fb2 --- /dev/null +++ b/build.gradle @@ -0,0 +1,22 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.0.0' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + version = VERSION_NAME + group = GROUP + + repositories { + mavenCentral() + } +} diff --git a/demo/build.gradle b/demo/build.gradle new file mode 100644 index 0000000..a0e4885 --- /dev/null +++ b/demo/build.gradle @@ -0,0 +1,27 @@ +apply plugin: 'com.android.application' + +dependencies { + compile project(':library') + compile "com.android.support:appcompat-v7:${project.ANDROID_SUPPORT_VERSION}@aar" +} + +android { + compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) + buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION + + defaultConfig { + applicationId "${project.GROUP}.${project.POM_ARTIFACT_ID}.demo" + minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) + targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) + versionName project.VERSION_NAME + versionCode Integer.parseInt(project.VERSION_CODE) + buildConfigField "String", "ABOUT_URL", "\"${project.POM_URL}\"" + } + + buildTypes { + release { + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' + } + } +} diff --git a/demo/proguard-rules.txt b/demo/proguard-rules.txt new file mode 100644 index 0000000..00c828e --- /dev/null +++ b/demo/proguard-rules.txt @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /opt/homebrew-cask/Caskroom/android-studio-bundle/0.4.2 build-133.970939/Android Studio.app/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml new file mode 100644 index 0000000..444ffc2 --- /dev/null +++ b/demo/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + diff --git a/demo/src/main/java/com/telly/mrvector/demo/BaseActivity.java b/demo/src/main/java/com/telly/mrvector/demo/BaseActivity.java new file mode 100644 index 0000000..e36c0f1 --- /dev/null +++ b/demo/src/main/java/com/telly/mrvector/demo/BaseActivity.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) Telly, Inc. and other contributors. + * + * 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.telly.mrvector.demo; + +import android.content.Intent; +import android.net.Uri; +import android.support.v7.app.ActionBarActivity; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; + +import static android.content.Intent.ACTION_VIEW; +import static android.content.Intent.CATEGORY_BROWSABLE; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static com.telly.mrvector.demo.BuildConfig.ABOUT_URL; + +abstract class BaseActivity extends ActionBarActivity { + private static final Uri ABOUT_URI = Uri.parse(ABOUT_URL); + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.main_menu, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int id = item.getItemId(); + switch (id) { + case R.id.action_about: + openAboutPage(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + T viewById(int id) { + return (T) super.findViewById(id); + } + + void openAboutPage() { + Intent intent = new Intent(ACTION_VIEW); + intent.addFlags(FLAG_ACTIVITY_NEW_TASK); + intent.setData(ABOUT_URI); + intent.addCategory(CATEGORY_BROWSABLE); + startActivity(intent); + } + +} diff --git a/demo/src/main/java/com/telly/mrvector/demo/BasicInflateActivity.java b/demo/src/main/java/com/telly/mrvector/demo/BasicInflateActivity.java new file mode 100644 index 0000000..0b669ff --- /dev/null +++ b/demo/src/main/java/com/telly/mrvector/demo/BasicInflateActivity.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) Telly, Inc. and other contributors. + * + * 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.telly.mrvector.demo; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageButton; + +import com.telly.mrvector.MrVector; + +import java.util.Random; + +public class BasicInflateActivity extends BaseActivity implements View.OnClickListener { + private static final Random sRandom = new Random(System.currentTimeMillis()); + private static final int[] sDrawables = { + R.drawable.logo, + R.drawable.vector_android + }; + private ImageButton mButton; + private int mLastResId; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_basic_inflate); + + mButton = viewById(R.id.button); + mButton.setOnClickListener(this); + nextDrawable(); + } + + @Override + public void onClick(View v) { + nextDrawable(); + } + + private void nextDrawable() { + int resId; + do { + resId = sDrawables[sRandom.nextInt(sDrawables.length)]; + } while (resId == mLastResId); + mLastResId = resId; + Drawable drawable = MrVector.inflate(getResources(), resId); + mButton.setImageDrawable(drawable); + } +} diff --git a/demo/src/main/java/com/telly/mrvector/demo/InflateFromLayoutActivity.java b/demo/src/main/java/com/telly/mrvector/demo/InflateFromLayoutActivity.java new file mode 100644 index 0000000..ac5b7b6 --- /dev/null +++ b/demo/src/main/java/com/telly/mrvector/demo/InflateFromLayoutActivity.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) Telly, Inc. and other contributors. + * + * 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.telly.mrvector.demo; + +import android.content.Context; +import android.os.Bundle; +import android.view.Menu; + +import com.telly.mrvector.MrVector; + +public class InflateFromLayoutActivity extends BaseActivity { + {{ // Must be static, Ideally you would register all the vector drawables at once + MrVector.register( + R.drawable.vector_android, + R.drawable.sample_vector_drawable + ); + }} + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_inflate_from_layout); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.layout_inflate_menu, menu); + return super.onCreateOptionsMenu(menu); + } + + @Override + protected void attachBaseContext(Context newBase) { + // This allows inflation magic + // Ideally you would do this in your BaseActivity or Application instead of per activity + super.attachBaseContext(MrVector.wrap(newBase)); + } + +} diff --git a/demo/src/main/res/drawable-hdpi/ic_launcher.png b/demo/src/main/res/drawable-hdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..0001cc0767b7f354e6ddfa1905dec28ae3482d66 GIT binary patch literal 5527 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 Z{~s3!0`5YinO*<@002ovPDHLkV1m)VlfM7} literal 0 HcmV?d00001 diff --git a/demo/src/main/res/drawable-mdpi/ic_launcher.png b/demo/src/main/res/drawable-mdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..ad79a36a509362378e3b79cffe565e300bbdb4d9 GIT binary patch literal 3416 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 B9+?0D literal 0 HcmV?d00001 diff --git a/demo/src/main/res/drawable-xxhdpi/ic_launcher.png b/demo/src/main/res/drawable-xxhdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..951d6077b3ffdade085d653fb00e75c7315f6835 GIT binary patch literal 12506 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 literal 0 HcmV?d00001 diff --git a/demo/src/main/res/drawable-xxxhdpi/ic_launcher.png b/demo/src/main/res/drawable-xxxhdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..915adc91baf1e9308804b48edb9f9f1e161cb4f0 GIT binary patch literal 16753 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%0DtyTm`%O zM}pBAf>{FKN_v3p&c(&{8$AFqXq8n~>5?h5Q~3d771MIM?8>1ORDFH`i%Ak10p$=P zRu!sSKcLh2D^cu?@hbaRet25+y;7}^;&!48!XWy)R zCj!~ujX5;*2XxA8wX!JijRL~Vy2jWC_l+Lpc)YI7IzVwCQ=8v?J^HK$U5`}Pq@x_S zK>We`E9=jDYK63)asZxI_S#H&pK%)(Vh1IL(8!gW^bYhI`S4*16W+cR-|`E&_&?^F)>t@TG9`l7_2`;>lp zh|c>&6WN~xn6mbNd;K^O$RY_qK16X93n{LmLpIjY!us*l+RQ3T%r5AVLk{t78AP7y zE3drLgv$Zk0p0;&7(Ro7$3E?`e1JbZ#yG!Z?SUA6UwX(y8B=CS90Ccf{9l&(`*Hx2 zBdpn`hwT*g?^L?nXXI_WK`Csj;uFwsnQ;LsG?%O7TxyfcWmx&VkmfPf?SOB@CwC!C zfk4A0mRQ33rj?XrOZ}<;qZHd$Q2u112;q+eo3jL)P=7D+$Cm@x>8QP`H`?G`)yNJD+R7szO`)mNm&@z)M z#J&TuNnxd9^5SYt~$VqETc*`P7xS8%x&{GM$#q1`kZ1?G&-bcbm@noayVQjC&(5gBV?5@Z*PcpPqGAw>^_4H#7pecUDD@z&12@@ z0pDhK_ADHhyh93X#SLHJw}~$jlSzDkOo4Ay4*>9)Hu8YW|!kgUPK*^PHvFA{0ie{380qia%U02?E&TjWR5N@05~Ce9%xOm0u|n z=_axWWzGe_cv_#*MA>(c=uMdqGLxF4`W*m}sh;7>iMbLf3f5U?o$4WA0lvZm4?JK934MWm2NpbHyW*@qNq|2*7Vv73?B6Th@H;Y< z-=c_w*@6qGeW}E-c7O3weyiaC1|c;OgRlr$ zF3_P+*OgU)`C7$#FDYdu(->H$#n7Oc_a( z-*O`@@Y|4w(Cqm)iC6n61m{CJ*kLyQ~oaD5=V~JFReaoeu>ikAM2@m@TC!7!v*_mcQ`4xqk z3-E{QA=kG@p%g{=b8bBH$RmFw*qS5Qg7RbbOl9|vK5I21kiC|*2*Cq81n?Bn?T|<` zOi9WURi5JWScURL0>mLTLrbL-*oUA!5q9rt%9*DDq*|Xt#b!juN+rqT)`X&rl_(Ff zhkJ*j&pgTbMb)EvLB^GRrT4urPBTlT7o#yhiR}J)Ab{lpCQ=kGpvb&o0?M{=pEnQ&&4Wa9|*-5*Lzaykh>wgP{J8F%>HG{qh zh?|v05>dCzu~>b2<@qnDChb+S`kX6Rnk!gxlie$pJ52J7S%^T~ub)D!#F;JuBzi%+ z9%OQeUNl6}tV`+ zN_hjzpbrQEGE1T_wRn(#6iJ0?dvz&^+W^8T2*#uGv4Z~M4j<6piG3n;nd~^MJk<_T zAL*q=XPG=HzXg5@+&=c$V{hmgzM(!}FGmz#H`ljn;LjQXxE>0zsUQL6*BmNkcp*Jh zt4yPt@4D-*=V>E56)=~pK43WkmC7i?NAcaP2HjU^B8@yv%hh@q_(Cqa=pxfPDXmqf z)N>qt^wDPja)|1j?{?PPJAL}}qP)GcWI;SdWft!h>~sruY`8PPZ*BfnDZcK_vl4;q zHT#J|aDvcD)~1TU`^wsKGMzz0Rde$X*u}sJ7*H@8?IrE`6$ji{jOap-Nl;v_W)>t& zzXYAh>uxB0mO#%l1MpOD_KBiRBF>gNHl+L}&<%f%Or94i7U^=q%v=G#Yx2xg^l(+?3aen)AJd#k^esp+VZH4@IZUW)`&Ek-5=8S)y43F=B{ z`W+xLt-t>IreK#vxuwfX+^81#&C8d<3lb;< zqeUjq+hp(mp(KPkf)(uiSbS`#v#s)$iRhCj0$8B7!E#0)5E2ZPNW%9vvAFY9!(~ea z%^?aE@`x95U!H6Ipc6W7f7&h8Vo(r@La+c7iZqHfxw0sh`q_l*+IdZFqR z0M0^=_jyi4I|2oL>nmje{92cBb}6qMljlP!v~`sJH(RhWTd-oYysX@xD%dUV+4G{l zt|-)J8r941(#vgA4#6Hh5WsQ)H)7Oqf>0l5l}T;5 z;`1+7=gCzRnOSorV1X3iqT`Hj*^W4B(rCX++%RGx)Zcex3H%b}=;`~i6!;?^eDJ|( zz=6!SuD!piNKc<<$`b)B64)GK6EFu7=s~;mphY#uTT#i=;}tHkj6x{7wYkI;v`e?BP1f#TpWtNTxybHW+bjii5SVBxQ@I3k*G5(oaeWI@>ZxN_(4?%uS*D_?p zAyNIIiXJTp0Y=ISa1P>-WYT%E4uCMZt`Bi2?&V^e#&k*$8L>u>Exx7ST?%oX?zN2=iqEoB)32|7dt zy2Jtt@)R#G#q1o3L!XnMpe9J*TY;LFD{F;`Ga`XR#lUK~b@8{t_dxs;9dne2(GRiX z5IqmECzSUA;vV@kiVARhY2w6*C##~{3lYk1f!_^s%N_V@jR5w3mJ{@uM;#<+*?8lP zmyrAYOj(E4m8{w;k1-pOY5#%f%Z@{WQouu-Pbwr+e2X08T6d}ukuW*Dgk{il8t;0! zF@V6|Lm?~K`CBCOKdY({C(3jE792|9@~+rpZT+cpd*ew&=_6{%0ixm%79WcQh7%}S zbg0CP0%DPr(bI(lTPoR}DsQ>R4wXX!U*N%{suyj%wIr7FoomB#%(#8ht2zxnl;B1Gq@wD#m?CAd#S5fnyyK9a^O9Eh)GC_aqN*?ubOWaf2jC zkOZKU99!?e?^1mQ@)gm4B=b*|$@p|-0ltnXCy~$Q@LI9Qwet@c<#&Ov8U)Jf+^C7v zRG2^~VA7WiBtRq*RXIdvzWv1sx+Ej_D42_I+FZDS4-zB+=p@HB0`Q0BFI!ZeyddD8 zA!YYEacgQ%&I{#zOZ6@AW9F>6sk5ReniT|Ra2*#3un@rs`nZ8)$abq2sB*(vNd@J(U z@P^zXJJY~m<^Yps5zaYo*o12)HJm{5WV)Ow#A2eKOjYVC z*U<0uQb>>lppzV1ufQJyK255p(sQc<>n=pDSH3r`OXo`7x4`czyKZ1x zO`Kdla8Pduz?xkou%%Yu1O^G<3XVurP#pWZatIwRZmOjMuVv^|cz52d$&UrO1n8{oIV=7T7;KE@(}s~lUW znL!3|gCSCo+oWWfoC7t%7^}|paD(9>f_{V5l5dxp7a1f-0?%u*`$dXQQ*ZKKeDE>aPdUI)w03=t%Lc@cv~1%6v37mANvUk*DX?kHzsxW7Lh)E=OB5dh$~BF}(ZdMdrJdK7`8>y7FKu5Mrz1QFtppb>%J0zMQUz*jy*k(^}3Jk1r=pQ;)?*DGP@2}j(q zjw~1WE#TLsyk80t8UX<;r;1vM5Q$u|s7*>3IzmAJ%CQA0XPc=y!=5skuCLgcR^n&i z2mmO`vQ_N7NP6U;a!Al?Iy7ZMCd>? zlIhgA?X7r}1{tDA&?vwkrS=dD$?+`!zOuDwI;|oNkKY;sz5#lG|9=LaIRLt)`c}!c zG5mF9;`9O1M&SVNvqTkSL?Tx|02$gq1S!HptXKphAQ~y$dPjMZHxVK%21mepf%%jh z;K~Ofk^r(rf+XkIhe>iyl4DC<-vT}2O{mx!F3A26tS8`qPvs=1s{YWOvg{!07{I6Z zvyfwU2>34WyW&nmQT-?|`5;QY9b*yKh&q}sH?SPRib$<`iSr@CHrs5otWpHOCPetM zxWdW`vMW-zhm=7LDy`&Uv)sAPe{JEMMZl>08ngMFQLjdK+Q#fsEqFdK9AP0R6i<0)G1L2l zUBGQfyelRNfLbIjbjiuKg7Okp6hf>lePL-GJ6!)?i~_)9vw;jyWQ!Ca1~oXDRC}>) zms~&xG3?}ACV*~%9e^j=K){a3K0^TiuFAT+Eh;}vRQ|=6UV7foxj2x!C` zo&!n`pz9g{Gk|S@+y#FAZ|9(-_)&lv%m`qgHOdiee332LXpzApg8iG%0tf*z;8zGK zyL&xD+)3tNef8BB6_-#L($d3)1dD4fp>r1zcUYLPB>}&QUy|t5Dg(zf%&?!u7sN(SYS7o2_Lvu>uu4k zh|vMyEfu#w9|eBnN^fgFMb`mfmrJ-*-v#_~NDu{ld!H!qb6hDTu*zwa3%Gwr&$HKZ zKXU=5M!aYf0pj19yGK5VQHqmTz%QvRJ4bM$+uzso7Er4TCqNf*E)nh22YrI$hq zm%5h%^m6C8D<}uJQL!cNxO7jo0G`!EfNWlRxqYm(zm;@==&GV-Q2Tqi|7Y`53&2(W YA3wXBzg=JX!~g&Q07*qoM6N<$f=Yy*y#N3J literal 0 HcmV?d00001 diff --git a/demo/src/main/res/drawable/e.xml b/demo/src/main/res/drawable/e.xml new file mode 100644 index 0000000..c9b2549 --- /dev/null +++ b/demo/src/main/res/drawable/e.xml @@ -0,0 +1,44 @@ + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/logo.xml b/demo/src/main/res/drawable/logo.xml new file mode 100644 index 0000000..dcf735e --- /dev/null +++ b/demo/src/main/res/drawable/logo.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/sample_vector_drawable.xml b/demo/src/main/res/drawable/sample_vector_drawable.xml new file mode 100644 index 0000000..bb41f08 --- /dev/null +++ b/demo/src/main/res/drawable/sample_vector_drawable.xml @@ -0,0 +1,48 @@ + + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/simple.xml b/demo/src/main/res/drawable/simple.xml new file mode 100644 index 0000000..9c134b6 --- /dev/null +++ b/demo/src/main/res/drawable/simple.xml @@ -0,0 +1,38 @@ + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/vector_android.xml b/demo/src/main/res/drawable/vector_android.xml new file mode 100644 index 0000000..058731e --- /dev/null +++ b/demo/src/main/res/drawable/vector_android.xml @@ -0,0 +1,35 @@ + + + + + + diff --git a/demo/src/main/res/layout/activity_basic_inflate.xml b/demo/src/main/res/layout/activity_basic_inflate.xml new file mode 100644 index 0000000..7a8bbe2 --- /dev/null +++ b/demo/src/main/res/layout/activity_basic_inflate.xml @@ -0,0 +1,30 @@ + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/layout/activity_inflate_from_layout.xml b/demo/src/main/res/layout/activity_inflate_from_layout.xml new file mode 100644 index 0000000..39d08f7 --- /dev/null +++ b/demo/src/main/res/layout/activity_inflate_from_layout.xml @@ -0,0 +1,30 @@ + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/menu/layout_inflate_menu.xml b/demo/src/main/res/menu/layout_inflate_menu.xml new file mode 100644 index 0000000..bcd5f7c --- /dev/null +++ b/demo/src/main/res/menu/layout_inflate_menu.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/demo/src/main/res/menu/main_menu.xml b/demo/src/main/res/menu/main_menu.xml new file mode 100644 index 0000000..9d84a39 --- /dev/null +++ b/demo/src/main/res/menu/main_menu.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/demo/src/main/res/values-w820dp/dimens.xml b/demo/src/main/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..4edec9f --- /dev/null +++ b/demo/src/main/res/values-w820dp/dimens.xml @@ -0,0 +1,3 @@ + + 64dp + diff --git a/demo/src/main/res/values/colors.xml b/demo/src/main/res/values/colors.xml new file mode 100644 index 0000000..eead1c6 --- /dev/null +++ b/demo/src/main/res/values/colors.xml @@ -0,0 +1,7 @@ + + + #ffa4c639 + #ff819b26 + #000000 + #000000 + \ No newline at end of file diff --git a/demo/src/main/res/values/dimens.xml b/demo/src/main/res/values/dimens.xml new file mode 100644 index 0000000..a9fd312 --- /dev/null +++ b/demo/src/main/res/values/dimens.xml @@ -0,0 +1,7 @@ + + 16dp + 16dp + 52dp + 20dp + 69dp + diff --git a/demo/src/main/res/values/integers.xml b/demo/src/main/res/values/integers.xml new file mode 100644 index 0000000..2d8fdec --- /dev/null +++ b/demo/src/main/res/values/integers.xml @@ -0,0 +1,7 @@ + + + 520 + 200 + 5 + 69 + \ No newline at end of file diff --git a/demo/src/main/res/values/strings.xml b/demo/src/main/res/values/strings.xml new file mode 100644 index 0000000..71b7bb4 --- /dev/null +++ b/demo/src/main/res/values/strings.xml @@ -0,0 +1,10 @@ + + + Mr. Vector Demo + Mr. Vector + About + Link + M32.2695312,59.671875 L232.296875,33.7929688 + M32.87501,1.01244444 C15.1159,1.91374444 1.0001,16.6554444 1,34.6218444 C1.0001,53.1681444 16.0465,68.2313444 34.60941,68.2313444 C53.17241,68.2313444 68.21871,53.1681444 68.21891,34.6218444 C68.21871,16.6556444 54.10341,1.91604444 36.34391,1.01244444 C35.77091,0.984444444 35.18961,1.01244444 34.60941,1.01244444 C34.02921,1.01244444 33.44781,0.984444444 32.87501,1.01244444 L32.87501,1.01244444 Z M33.15641,5.93434444 C33.64521,5.90934444 34.11431,5.93434444 34.60941,5.93434444 C35.10461,5.93434444 35.57351,5.90934444 36.06251,5.93434444 C51.22131,6.70584444 63.29661,19.2869444 63.29691,34.6218444 C63.29691,50.4519444 50.45361,63.2627444 34.60941,63.2624444 C18.7652,63.2624444 5.9222,50.4519444 5.9219,34.6218444 C5.9219,19.2869444 17.9974,6.70444444 33.15641,5.93434444 L33.15641,5.93434444 Z M23.92191,21.3563444 C21.58401,21.3563444 19.70321,23.2392444 19.70321,25.5749444 C19.70321,27.9107444 21.58401,29.8406444 23.92191,29.8406444 C26.25971,29.8406444 28.14071,27.9107444 28.14071,25.5749444 C28.14071,23.2392444 26.25971,21.3563444 23.92191,21.3563444 L23.92191,21.3563444 Z M45.29691,21.3563444 C42.95921,21.3563444 41.07821,23.2392444 41.07821,25.5749444 C41.07821,27.9107444 42.95921,29.8406444 45.29691,29.8406444 C47.63481,29.8406444 49.51571,27.9107444 49.51571,25.5749444 C49.51571,23.2392444 47.63481,21.3563444 45.29691,21.3563444 L45.29691,21.3563444 Z + M29.226855,30.54045 C25.574755,30.54045 24.281655,34.01055 21.323655,35.30965 C18.365355,36.60885 14.456755,36.56955 10.921855,33.94705 C10.921855,39.24925 17.892155,44.84835 24.911855,44.84835 C30.176455,44.84835 33.340355,42.33365 34.586855,39.35225 C35.833355,42.33365 38.997055,44.84835 44.261555,44.84835 C51.281455,44.84835 58.297055,39.24925 58.297055,33.94705 C54.762355,36.56955 50.853455,36.60885 47.895455,35.30965 C44.937355,34.01055 43.598955,30.54045 39.946555,30.54045 C37.373355,30.54045 35.493155,31.38725 34.586855,33.31115 C33.680555,31.38725 31.800155,30.54045 29.226855,30.54045 L29.226855,30.54045 Z + diff --git a/demo/src/main/res/values/styles.xml b/demo/src/main/res/values/styles.xml new file mode 100644 index 0000000..2117c31 --- /dev/null +++ b/demo/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/extras/logo.sketch b/extras/logo.sketch new file mode 100644 index 0000000000000000000000000000000000000000..1a3fa7bc9967ba2e24527f1a92de7a93a5161ba2 GIT binary patch literal 40960 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 Rec-PvZ{R5U{@;wie*xS9O>zJL literal 0 HcmV?d00001 diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..1101e44 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,25 @@ +VERSION_NAME=0.1.0 +VERSION_CODE=1 +GROUP=com.telly + +POM_NAME=Mr. Vector Library +POM_ARTIFACT_ID=mrvector +POM_PACKAGING=aar + +POM_DESCRIPTION=Mr. Vector (VectorDrawableCompat): A 14+ backport of VectorDrawable +POM_URL=https://github.com/telly/MrVector +POM_SCM_URL=https://github.com/telly/MrVector +POM_SCM_CONNECTION=scm:git@github.com:telly/MrVector.git +POM_SCM_DEV_CONNECTION=scm:git@github.com:telly/MrVector.git +POM_LICENCE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo +POM_DEVELOPER_ID=eveliotc +POM_DEVELOPER_NAME=Evelio Tarazona Caceres + +ANDROID_BUILD_MIN_SDK_VERSION=14 +ANDROID_BUILD_TARGET_SDK_VERSION=21 +ANDROID_BUILD_SDK_VERSION=21 +ANDROID_BUILD_TOOLS_VERSION=21.1.2 + +ANDROID_SUPPORT_VERSION=21.0.3 \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c0fb64a8698b08ecc4158d828ca593c4928e9dd GIT binary patch literal 49896 zcmagFb986H(k`5d^NVfUwr$(C?M#x1ZQHiZiEVpg+jrjgoQrerx!>1o_ul)D>ebz~ zs=Mmxr&>W81QY-S1PKWQ%N-;H^tS;2*XwVA`dej1RRn1z<;3VgfE4~kaG`A%QSPsR z#ovnZe+tS9%1MfeDyz`RirvdjPRK~p(#^q2(^5@O&NM19EHdvN-A&StN>0g6QA^VN z0Gx%Gq#PD$QMRFzmK+utjS^Y1F0e8&u&^=w5K<;4Rz|i3A=o|IKLY+g`iK6vfr9?+ z-`>gmU&i?FGSL5&F?TXFu`&Js6h;15QFkXp2M1H9|Eq~bpov-GU(uz%mH0n55wUl- zv#~ccAz`F5wlQ>e_KlJS3@{)B?^v*EQM=IxLa&76^y51a((wq|2-`qON>+4dLc{Oo z51}}o^Zen(oAjxDK7b++9_Yg`67p$bPo3~BCpGM7uAWmvIhWc5Gi+gQZ|Pwa-Gll@<1xmcPy z|NZmu6m)g5Ftu~BG&Xdxclw7Cij{xbBMBn-LMII#Slp`AElb&2^Hw+w>(3crLH!;I zN+Vk$D+wP1#^!MDCiad@vM>H#6+`Ct#~6VHL4lzmy;lSdk>`z6)=>Wh15Q2)dQtGqvn0vJU@+(B5{MUc*qs4!T+V=q=wy)<6$~ z!G>e_4dN@lGeF_$q9`Ju6Ncb*x?O7=l{anm7Eahuj_6lA{*#Gv*TaJclevPVbbVYu z(NY?5q+xxbO6%g1xF0r@Ix8fJ~u)VRUp`S%&rN$&e!Od`~s+64J z5*)*WSi*i{k%JjMSIN#X;jC{HG$-^iX+5f5BGOIHWAl*%15Z#!xntpk($-EGKCzKa zT7{siZ9;4TICsWQ$pu&wKZQTCvpI$Xvzwxoi+XkkpeE&&kFb!B?h2hi%^YlXt|-@5 zHJ~%AN!g_^tmn1?HSm^|gCE#!GRtK2(L{9pL#hp0xh zME}|DB>(5)`iE7CM)&_+S}-Bslc#@B5W4_+k4Cp$l>iVyg$KP>CN?SVGZ(&02>iZK zB<^HP$g$Lq*L$BWd?2(F?-MUbNWTJVQdW7$#8a|k_30#vHAD1Z{c#p;bETk0VnU5A zBgLe2HFJ3032$G<`m*OB!KM$*sdM20jm)It5OSru@tXpK5LT>#8)N!*skNu1$TpIw zufjjdp#lyH5bZ%|Iuo|iu9vG1HrIVWLH>278xo>aVBkPN3V$~!=KnlXQ4eDqS7%E% zQ!z^$Q$b^6Q)g#cLpwur(|<0gWHo6A6jc;n`t(V9T;LzTAU{IAu*uEQ%Ort1k+Kn+f_N`9|bxYC+~Z1 zCC1UCWv*Orx$_@ydv9mIe(liLfOr7mhbV@tKw{6)q^1DH1nmvZ0cj215R<~&I<4S| zgnr;9Cdjqpz#o8i0CQjtl`}{c*P)aSdH|abxGdrR)-3z+02-eX(k*B)Uqv6~^nh** z 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 \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/library/build.gradle b/library/build.gradle new file mode 100644 index 0000000..f7c2a6d --- /dev/null +++ b/library/build.gradle @@ -0,0 +1,26 @@ +apply plugin: 'com.android.library' + +dependencies { + compile "com.android.support:support-annotations:${project.ANDROID_SUPPORT_VERSION}" + compile "com.android.support:support-v4:${project.ANDROID_SUPPORT_VERSION}@aar" +} + +android { + compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION) + buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION + + defaultConfig { + minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION) + targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION) + versionName project.VERSION_NAME + versionCode Integer.parseInt(project.VERSION_CODE) + } + + buildTypes { + release { + proguardFiles getDefaultProguardFile('proguard-android.pro'), 'proguard-rules.txt' + } + } +} + +apply from: './gradle-mvn-push.gradle' \ No newline at end of file diff --git a/library/gradle-mvn-push.gradle b/library/gradle-mvn-push.gradle new file mode 100644 index 0000000..27fff0d --- /dev/null +++ b/library/gradle-mvn-push.gradle @@ -0,0 +1,114 @@ +/* + * Copyright 2013 Chris Banes + * + * 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. + */ + +apply plugin: 'maven' +apply plugin: 'signing' + +def isReleaseBuild() { + return VERSION_NAME.contains("SNAPSHOT") == false +} + +def getReleaseRepositoryUrl() { + return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL + : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" +} + +def getSnapshotRepositoryUrl() { + return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL + : "https://oss.sonatype.org/content/repositories/snapshots/" +} + +def getRepositoryUsername() { + return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" +} + +def getRepositoryPassword() { + return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" +} + +afterEvaluate { project -> + uploadArchives { + repositories { + mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + pom.groupId = GROUP + pom.artifactId = POM_ARTIFACT_ID + pom.version = VERSION_NAME + + repository(url: getReleaseRepositoryUrl()) { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + snapshotRepository(url: getSnapshotRepositoryUrl()) { + authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) + } + + pom.project { + name POM_NAME + packaging POM_PACKAGING + description POM_DESCRIPTION + url POM_URL + + scm { + url POM_SCM_URL + connection POM_SCM_CONNECTION + developerConnection POM_SCM_DEV_CONNECTION + } + + licenses { + license { + name POM_LICENCE_NAME + url POM_LICENCE_URL + distribution POM_LICENCE_DIST + } + } + + developers { + developer { + id POM_DEVELOPER_ID + name POM_DEVELOPER_NAME + } + } + } + } + } + } + + signing { + required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives + } + + task androidJavadocs(type: Javadoc) { + source = android.sourceSets.main.java.srcDirs + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + } + + task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { + classifier = 'javadoc' + from androidJavadocs.destinationDir + } + + task androidSourcesJar(type: Jar) { + classifier = 'sources' + from android.sourceSets.main.java.srcDirs + } + + artifacts { + archives androidSourcesJar + archives androidJavadocsJar + } +} diff --git a/library/proguard-rules.txt b/library/proguard-rules.txt new file mode 100644 index 0000000..cb8998d --- /dev/null +++ b/library/proguard-rules.txt @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} \ No newline at end of file diff --git a/library/src/main/AndroidManifest.xml b/library/src/main/AndroidManifest.xml new file mode 100644 index 0000000..7845c95 --- /dev/null +++ b/library/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/library/src/main/java/android/content/res/MrResources.java b/library/src/main/java/android/content/res/MrResources.java new file mode 100644 index 0000000..c72f0ff --- /dev/null +++ b/library/src/main/java/android/content/res/MrResources.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) Telly, Inc. and other contributors. + * + * 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 android.content.res; + +import android.annotation.TargetApi; +import android.graphics.drawable.Drawable; +import android.os.Build; + +import com.telly.mrvector.MrVector; + +/** + * @hide + */ +public class MrResources extends Resources { + private final Resources mResources; + + public MrResources(Resources resources) { + super(resources.getAssets(), resources.getDisplayMetrics(), resources.getConfiguration()); + mResources = resources; + } + + public boolean oldFor(Resources superResources) { + return superResources != mResources; + } + + @Override + public Drawable getDrawable(int id) throws NotFoundException { + Drawable mr = lookup(id); + if (mr != null) { + return mr; + } + return super.getDrawable(id); + } + + @Override + @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) + public Drawable getDrawableForDensity(int id, int density) throws NotFoundException { + Drawable mr = lookup(id, density); + if (mr != null) { + return mr; + } + return super.getDrawableForDensity(id, density); + } + + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Drawable getDrawable(int id, Theme theme) throws NotFoundException { + return super.getDrawable(id, theme); + } + + + @Override + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public Drawable getDrawableForDensity(int id, int density, Theme theme) { + return super.getDrawableForDensity(id, density, theme); + } + + /* TODO some sorcery to make the calls from TypedArray work + + Drawable loadDrawable(TypedValue value, int id) throws Resources.NotFoundException { + Log.d("vector", "loadDrawable@2 " + id); + return super.loadDrawable(value, id); + } + + Drawable loadDrawable(TypedValue value, int id, Theme theme) throws Resources.NotFoundException { + Log.d("vector", "loadDrawable@3 " + id); + return super.loadDrawable(value, id, theme); + } + */ + + + private Drawable lookup(int id) { + return MrVector.lookup(mResources, id, 0, false); + } + + private Drawable lookup(int id, int density) { + return MrVector.lookup(mResources, id, density, true); + } + +} \ No newline at end of file diff --git a/library/src/main/java/android/support/v4/util/IntSet.java b/library/src/main/java/android/support/v4/util/IntSet.java new file mode 100644 index 0000000..5de4b0b --- /dev/null +++ b/library/src/main/java/android/support/v4/util/IntSet.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) Telly, Inc. and other contributors. + * + * 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 android.support.v4.util; + +import java.util.Arrays; + +import static android.support.v4.util.ContainerHelpers.binarySearch; +import static android.support.v4.util.ContainerHelpers.idealIntArraySize; + +public class IntSet implements Cloneable { + private static final int DELETED = 0; + private int[] mElements; + private int mSize; + private boolean mContainsDeleted; + + /** + * Creates a new ResArray containing no mappings. + */ + public IntSet() { + this(10); + } + + /** + * Creates a new ResArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. If you supply an initial capacity of 0, the + * sparse array will be initialized with a light-weight representation + * not requiring any additional array allocations. + */ + public IntSet(int initialCapacity) { + if (initialCapacity == 0) { + mElements = ContainerHelpers.EMPTY_INTS; + } else { + mElements = new int[idealIntArraySize(initialCapacity)]; + } + mSize = 0; + } + + @Override + public IntSet clone() { + IntSet clone = null; + try { + clone = (IntSet) super.clone(); + clone.mElements = mElements.clone(); + } catch (CloneNotSupportedException cnse) { + /* ignore */ + } + return clone; + } + + public void remove(int element) { + if (element == DELETED) { + mContainsDeleted = false; + return; + } + + int i = binarySearch(mElements, mSize, element); + + if (i >= 0) { + mElements[i] = DELETED; + } + } + + public void add(int element) { + if (element == DELETED) { + mContainsDeleted = true; + return; + } + + if (contains(element)) { + return; // already there + } else { + mElements = append(mElements, mSize, element); + mSize++; + } + } + + public void addAll(int[] elements) { + if (elements == null) { + return; + } + for (int element : elements) { + add(element); + } + } + + public boolean contains(int element) { + if (element == DELETED) { + return mContainsDeleted; + } + return binarySearch(mElements, mSize, element) >= 0; + } + + static int[] append(int[] array, int currentSize, int element) { + assert currentSize <= array.length; + + if (currentSize + 1 > array.length) { + int[] newArray = new int[idealIntArraySize(currentSize)]; + System.arraycopy(array, 0, newArray, 0, currentSize); + array = newArray; + } + array[currentSize] = element; + return array; + } + + public int size() { + return mSize; + } + + public void clear() { + mSize = 0; + Arrays.fill(mElements, DELETED); + } + + @Override + public String toString() { + if (size() <= 0) { + return "{}"; + } + + StringBuilder buffer = new StringBuilder(mSize * 28); + buffer.append('{'); + for (int i = 0; i < mSize; i++) { + if (i > 0) { + buffer.append(", "); + } + + int element = mElements[i]; + buffer.append(element); + } + buffer.append('}'); + return buffer.toString(); + } +} diff --git a/library/src/main/java/com/telly/mrvector/MrVector.java b/library/src/main/java/com/telly/mrvector/MrVector.java new file mode 100644 index 0000000..cf8cd7c --- /dev/null +++ b/library/src/main/java/com/telly/mrvector/MrVector.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) Telly, Inc. and other contributors. + * + * 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.telly.mrvector; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.res.MrResources; +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.support.annotation.DrawableRes; +import android.support.v4.util.IntSet; +import android.util.Log; + +import static com.telly.mrvector.Utils.LOLLIPOP_PLUS; + +/** + * Facade for creating Drawables in a compatible way + */ +public class MrVector { + /** + * Inflates a drawable, using framework implementation when available + * @param resources + * Resources to use for inflation + * @param resId + * drawable resource + * @return + *

Framework {@link android.graphics.drawable.VectorDrawable} if running lollipop or later.

+ *

{@link com.telly.mrvector.VectorDrawable} otherwise.

+ * + * @see #inflateCompatOnly(android.content.res.Resources, int) + */ + public static Drawable inflate(Resources resources, @DrawableRes int resId) { + if (LOLLIPOP_PLUS) { + return resources.getDrawable(resId); + } else { + return inflateCompatOnly(resources, resId); + } + } + + /** + * Inflates a drawable, using {@link com.telly.mrvector.VectorDrawable} implementation always. + * @param resources + * Resources to use for inflation + * @param resId + * drawable resource + * @return + *

Inflated instance of {@link com.telly.mrvector.VectorDrawable}.

+ * + * @see #inflate(android.content.res.Resources, int) + */ + public static Drawable inflateCompatOnly(Resources resources, @DrawableRes int resId) { + return VectorDrawable.create(resources, resId); + } + + private static IntSet sVectorResources; + public static void register(@DrawableRes int... resources) { + if (resources == null || resources.length < 1) { + return; + } + if (sVectorResources == null) { + sVectorResources = new IntSet(resources.length); + } + sVectorResources.addAll(resources); + } + + public static Context wrap(Context context) { + return new MrResourcesContext(context); + } + + static class MrResourcesContext extends ContextWrapper { + private MrResources mMrResources; + public MrResourcesContext(Context base) { + super(base); + } + + @Override + public Resources getResources() { + final Resources superResources = super.getResources(); + if (mMrResources == null || mMrResources.oldFor(superResources)) { + mMrResources = new MrResources(superResources); + } + return mMrResources; + } + } + + /** + * @hide + */ + public static Drawable lookup(Resources res, int id, int density, boolean ignoreDensity) { + Log.d(VectorDrawable.LOGTAG, "Looking up res " + id); + if (sVectorResources == null || !sVectorResources.contains(id)) { + Log.d(VectorDrawable.LOGTAG, "Could not find res " + id); + return null; + } + // TODO support density + Log.d(VectorDrawable.LOGTAG, "Inflating res " + id); + return inflateCompatOnly(res, id); + } + +} diff --git a/library/src/main/java/com/telly/mrvector/PathParser.java b/library/src/main/java/com/telly/mrvector/PathParser.java new file mode 100644 index 0000000..e5220e0 --- /dev/null +++ b/library/src/main/java/com/telly/mrvector/PathParser.java @@ -0,0 +1,654 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * 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.telly.mrvector; + +import android.annotation.TargetApi; +import android.graphics.Path; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; + +import static android.os.Build.VERSION_CODES.LOLLIPOP; + +/** + * @hide + */ +@TargetApi(LOLLIPOP) +public class PathParser { + static final String LOGTAG = PathParser.class.getSimpleName(); + + /** + * @param pathData The string representing a path, the same as "d" string in svg file. + * @return the generated Path object. + */ + public static Path createPathFromPathData(String pathData) { + Path path = new Path(); + PathDataNode[] nodes = createNodesFromPathData(pathData); + if (nodes != null) { + PathDataNode.nodesToPath(nodes, path); + return path; + } + return null; + } + + /** + * @param pathData The string representing a path, the same as "d" string in svg file. + * @return an array of the PathDataNode. + */ + public static PathDataNode[] createNodesFromPathData(String pathData) { + if (pathData == null) { + return null; + } + int start = 0; + int end = 1; + + ArrayList list = new ArrayList(); + while (end < pathData.length()) { + end = nextStart(pathData, end); + String s = pathData.substring(start, end).trim(); + if (s.length() > 0) { + float[] val = getFloats(s); + addNode(list, s.charAt(0), val); + } + + start = end; + end++; + } + if ((end - start) == 1 && start < pathData.length()) { + addNode(list, pathData.charAt(start), new float[0]); + } + return list.toArray(new PathDataNode[list.size()]); + } + + /** + * @param source The array of PathDataNode to be duplicated. + * @return a deep copy of the source. + */ + public static PathDataNode[] deepCopyNodes(PathDataNode[] source) { + if (source == null) { + return null; + } + PathDataNode[] copy = new PathParser.PathDataNode[source.length]; + for (int i = 0; i < source.length; i ++) { + copy[i] = new PathDataNode(source[i]); + } + return copy; + } + + /** + * @param nodesFrom The source path represented in an array of PathDataNode + * @param nodesTo The target path represented in an array of PathDataNode + * @return whether the nodesFrom can morph into nodesTo + */ + public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) { + if (nodesFrom == null || nodesTo == null) { + return false; + } + + if (nodesFrom.length != nodesTo.length) { + return false; + } + + for (int i = 0; i < nodesFrom.length; i ++) { + if (nodesFrom[i].mType != nodesTo[i].mType + || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) { + return false; + } + } + return true; + } + + /** + * Update the target's data to match the source. + * Before calling this, make sure canMorph(target, source) is true. + * + * @param target The target path represented in an array of PathDataNode + * @param source The source path represented in an array of PathDataNode + */ + public static void updateNodes(PathDataNode[] target, PathDataNode[] source) { + for (int i = 0; i < source.length; i ++) { + target[i].mType = source[i].mType; + for (int j = 0; j < source[i].mParams.length; j ++) { + target[i].mParams[j] = source[i].mParams[j]; + } + } + } + + private static int nextStart(String s, int end) { + char c; + + while (end < s.length()) { + c = s.charAt(end); + if (((c - 'A') * (c - 'Z') <= 0) || (((c - 'a') * (c - 'z') <= 0))) { + return end; + } + end++; + } + return end; + } + + private static void addNode(ArrayList list, char cmd, float[] val) { + list.add(new PathDataNode(cmd, val)); + } + + private static class ExtractFloatResult { + // We need to return the position of the next separator and whether the + // next float starts with a '-'. + int mEndPosition; + boolean mEndWithNegSign; + } + + /** + * Parse the floats in the string. + * This is an optimized version of parseFloat(s.split(",|\\s")); + * + * @param s the string containing a command and list of floats + * @return array of floats + */ + private static float[] getFloats(String s) { + if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') { + return new float[0]; + } + try { + float[] results = new float[s.length()]; + int count = 0; + int startPosition = 1; + int endPosition = 0; + + ExtractFloatResult result = new ExtractFloatResult(); + int totalLength = s.length(); + + // The startPosition should always be the first character of the + // current number, and endPosition is the character after the current + // number. + while (startPosition < totalLength) { + extract(s, startPosition, result); + endPosition = result.mEndPosition; + + if (startPosition < endPosition) { + results[count++] = Float.parseFloat( + s.substring(startPosition, endPosition)); + } + + if (result.mEndWithNegSign) { + // Keep the '-' sign with next number. + startPosition = endPosition; + } else { + startPosition = endPosition + 1; + } + } + return Arrays.copyOf(results, count); + } catch (NumberFormatException e) { + Log.e(LOGTAG, "error in parsing \"" + s + "\""); + throw e; + } + } + + /** + * Calculate the position of the next comma or space or negative sign + * @param s the string to search + * @param start the position to start searching + * @param result the result of the extraction, including the position of the + * the starting position of next number, whether it is ending with a '-'. + */ + private static void extract(String s, int start, ExtractFloatResult result) { + // Now looking for ' ', ',' or '-' from the start. + int currentIndex = start; + boolean foundSeparator = false; + result.mEndWithNegSign = false; + for (; currentIndex < s.length(); currentIndex++) { + char currentChar = s.charAt(currentIndex); + switch (currentChar) { + case ' ': + case ',': + foundSeparator = true; + break; + case '-': + if (currentIndex != start) { + foundSeparator = true; + result.mEndWithNegSign = true; + } + break; + } + if (foundSeparator) { + break; + } + } + // When there is nothing found, then we put the end position to the end + // of the string. + result.mEndPosition = currentIndex; + } + + /** + * Each PathDataNode represents one command in the "d" attribute of the svg + * file. + * An array of PathDataNode can represent the whole "d" attribute. + */ + public static class PathDataNode { + private char mType; + private float[] mParams; + + private PathDataNode(char type, float[] params) { + mType = type; + mParams = params; + } + + private PathDataNode(PathDataNode n) { + mType = n.mType; + mParams = Arrays.copyOf(n.mParams, n.mParams.length); + } + + /** + * Convert an array of PathDataNode to Path. + * + * @param node The source array of PathDataNode. + * @param path The target Path object. + */ + public static void nodesToPath(PathDataNode[] node, Path path) { + float[] current = new float[4]; + char previousCommand = 'm'; + for (int i = 0; i < node.length; i++) { + addCommand(path, current, previousCommand, node[i].mType, node[i].mParams); + previousCommand = node[i].mType; + } + } + + /** + * The current PathDataNode will be interpolated between the + * nodeFrom and nodeTo according to the + * fraction. + * + * @param nodeFrom The start value as a PathDataNode. + * @param nodeTo The end value as a PathDataNode + * @param fraction The fraction to interpolate. + */ + public void interpolatePathDataNode(PathDataNode nodeFrom, + PathDataNode nodeTo, float fraction) { + for (int i = 0; i < nodeFrom.mParams.length; i++) { + mParams[i] = nodeFrom.mParams[i] * (1 - fraction) + + nodeTo.mParams[i] * fraction; + } + } + + private static void addCommand(Path path, float[] current, + char previousCmd, char cmd, float[] val) { + + int incr = 2; + float currentX = current[0]; + float currentY = current[1]; + float ctrlPointX = current[2]; + float ctrlPointY = current[3]; + float reflectiveCtrlPointX; + float reflectiveCtrlPointY; + + switch (cmd) { + case 'z': + case 'Z': + path.close(); + return; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + incr = 2; + break; + case 'h': + case 'H': + case 'v': + case 'V': + incr = 1; + break; + case 'c': + case 'C': + incr = 6; + break; + case 's': + case 'S': + case 'q': + case 'Q': + incr = 4; + break; + case 'a': + case 'A': + incr = 7; + break; + } + for (int k = 0; k < val.length; k += incr) { + switch (cmd) { + case 'm': // moveto - Start a new sub-path (relative) + path.rMoveTo(val[k + 0], val[k + 1]); + currentX += val[k + 0]; + currentY += val[k + 1]; + break; + case 'M': // moveto - Start a new sub-path + path.moveTo(val[k + 0], val[k + 1]); + currentX = val[k + 0]; + currentY = val[k + 1]; + break; + case 'l': // lineto - Draw a line from the current point (relative) + path.rLineTo(val[k + 0], val[k + 1]); + currentX += val[k + 0]; + currentY += val[k + 1]; + break; + case 'L': // lineto - Draw a line from the current point + path.lineTo(val[k + 0], val[k + 1]); + currentX = val[k + 0]; + currentY = val[k + 1]; + break; + case 'z': // closepath - Close the current subpath + case 'Z': // closepath - Close the current subpath + path.close(); + break; + case 'h': // horizontal lineto - Draws a horizontal line (relative) + path.rLineTo(val[k + 0], 0); + currentX += val[k + 0]; + break; + case 'H': // horizontal lineto - Draws a horizontal line + path.lineTo(val[k + 0], currentY); + currentX = val[k + 0]; + break; + case 'v': // vertical lineto - Draws a vertical line from the current point (r) + path.rLineTo(0, val[k + 0]); + currentY += val[k + 0]; + break; + case 'V': // vertical lineto - Draws a vertical line from the current point + path.lineTo(currentX, val[k + 0]); + currentY = val[k + 0]; + break; + case 'c': // curveto - Draws a cubic Bézier curve (relative) + path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], + val[k + 4], val[k + 5]); + + ctrlPointX = currentX + val[k + 2]; + ctrlPointY = currentY + val[k + 3]; + currentX += val[k + 4]; + currentY += val[k + 5]; + + break; + case 'C': // curveto - Draws a cubic Bézier curve + path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3], + val[k + 4], val[k + 5]); + currentX = val[k + 4]; + currentY = val[k + 5]; + ctrlPointX = val[k + 2]; + ctrlPointY = val[k + 3]; + break; + case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp) + reflectiveCtrlPointX = 0; + reflectiveCtrlPointY = 0; + if (previousCmd == 'c' || previousCmd == 's' + || previousCmd == 'C' || previousCmd == 'S') { + reflectiveCtrlPointX = currentX - ctrlPointX; + reflectiveCtrlPointY = currentY - ctrlPointY; + } + path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, + val[k + 0], val[k + 1], + val[k + 2], val[k + 3]); + + ctrlPointX = currentX + val[k + 0]; + ctrlPointY = currentY + val[k + 1]; + currentX += val[k + 2]; + currentY += val[k + 3]; + break; + case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp) + reflectiveCtrlPointX = currentX; + reflectiveCtrlPointY = currentY; + if (previousCmd == 'c' || previousCmd == 's' + || previousCmd == 'C' || previousCmd == 'S') { + reflectiveCtrlPointX = 2 * currentX - ctrlPointX; + reflectiveCtrlPointY = 2 * currentY - ctrlPointY; + } + path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY, + val[k + 0], val[k + 1], val[k + 2], val[k + 3]); + ctrlPointX = val[k + 0]; + ctrlPointY = val[k + 1]; + currentX = val[k + 2]; + currentY = val[k + 3]; + break; + case 'q': // Draws a quadratic Bézier (relative) + path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); + ctrlPointX = currentX + val[k + 0]; + ctrlPointY = currentY + val[k + 1]; + currentX += val[k + 2]; + currentY += val[k + 3]; + break; + case 'Q': // Draws a quadratic Bézier + path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]); + ctrlPointX = val[k + 0]; + ctrlPointY = val[k + 1]; + currentX = val[k + 2]; + currentY = val[k + 3]; + break; + case 't': // Draws a quadratic Bézier curve(reflective control point)(relative) + reflectiveCtrlPointX = 0; + reflectiveCtrlPointY = 0; + if (previousCmd == 'q' || previousCmd == 't' + || previousCmd == 'Q' || previousCmd == 'T') { + reflectiveCtrlPointX = currentX - ctrlPointX; + reflectiveCtrlPointY = currentY - ctrlPointY; + } + path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, + val[k + 0], val[k + 1]); + ctrlPointX = currentX + reflectiveCtrlPointX; + ctrlPointY = currentY + reflectiveCtrlPointY; + currentX += val[k + 0]; + currentY += val[k + 1]; + break; + case 'T': // Draws a quadratic Bézier curve (reflective control point) + reflectiveCtrlPointX = currentX; + reflectiveCtrlPointY = currentY; + if (previousCmd == 'q' || previousCmd == 't' + || previousCmd == 'Q' || previousCmd == 'T') { + reflectiveCtrlPointX = 2 * currentX - ctrlPointX; + reflectiveCtrlPointY = 2 * currentY - ctrlPointY; + } + path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY, + val[k + 0], val[k + 1]); + ctrlPointX = reflectiveCtrlPointX; + ctrlPointY = reflectiveCtrlPointY; + currentX = val[k + 0]; + currentY = val[k + 1]; + break; + case 'a': // Draws an elliptical arc + // (rx ry x-axis-rotation large-arc-flag sweep-flag x y) + drawArc(path, + currentX, + currentY, + val[k + 5] + currentX, + val[k + 6] + currentY, + val[k + 0], + val[k + 1], + val[k + 2], + val[k + 3] != 0, + val[k + 4] != 0); + currentX += val[k + 5]; + currentY += val[k + 6]; + ctrlPointX = currentX; + ctrlPointY = currentY; + break; + case 'A': // Draws an elliptical arc + drawArc(path, + currentX, + currentY, + val[k + 5], + val[k + 6], + val[k + 0], + val[k + 1], + val[k + 2], + val[k + 3] != 0, + val[k + 4] != 0); + currentX = val[k + 5]; + currentY = val[k + 6]; + ctrlPointX = currentX; + ctrlPointY = currentY; + break; + } + previousCmd = cmd; + } + current[0] = currentX; + current[1] = currentY; + current[2] = ctrlPointX; + current[3] = ctrlPointY; + } + + private static void drawArc(Path p, + float x0, + float y0, + float x1, + float y1, + float a, + float b, + float theta, + boolean isMoreThanHalf, + boolean isPositiveArc) { + + /* Convert rotation angle from degrees to radians */ + double thetaD = Math.toRadians(theta); + /* Pre-compute rotation matrix entries */ + double cosTheta = Math.cos(thetaD); + double sinTheta = Math.sin(thetaD); + /* Transform (x0, y0) and (x1, y1) into unit space */ + /* using (inverse) rotation, followed by (inverse) scale */ + double x0p = (x0 * cosTheta + y0 * sinTheta) / a; + double y0p = (-x0 * sinTheta + y0 * cosTheta) / b; + double x1p = (x1 * cosTheta + y1 * sinTheta) / a; + double y1p = (-x1 * sinTheta + y1 * cosTheta) / b; + + /* Compute differences and averages */ + double dx = x0p - x1p; + double dy = y0p - y1p; + double xm = (x0p + x1p) / 2; + double ym = (y0p + y1p) / 2; + /* Solve for intersecting unit circles */ + double dsq = dx * dx + dy * dy; + if (dsq == 0.0) { + Log.w(LOGTAG, " Points are coincident"); + return; /* Points are coincident */ + } + double disc = 1.0 / dsq - 1.0 / 4.0; + if (disc < 0.0) { + Log.w(LOGTAG, "Points are too far apart " + dsq); + float adjust = (float) (Math.sqrt(dsq) / 1.99999); + drawArc(p, x0, y0, x1, y1, a * adjust, + b * adjust, theta, isMoreThanHalf, isPositiveArc); + return; /* Points are too far apart */ + } + double s = Math.sqrt(disc); + double sdx = s * dx; + double sdy = s * dy; + double cx; + double cy; + if (isMoreThanHalf == isPositiveArc) { + cx = xm - sdy; + cy = ym + sdx; + } else { + cx = xm + sdy; + cy = ym - sdx; + } + + double eta0 = Math.atan2((y0p - cy), (x0p - cx)); + + double eta1 = Math.atan2((y1p - cy), (x1p - cx)); + + double sweep = (eta1 - eta0); + if (isPositiveArc != (sweep >= 0)) { + if (sweep > 0) { + sweep -= 2 * Math.PI; + } else { + sweep += 2 * Math.PI; + } + } + + cx *= a; + cy *= b; + double tcx = cx; + cx = cx * cosTheta - cy * sinTheta; + cy = tcx * sinTheta + cy * cosTheta; + + arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep); + } + + /** + * Converts an arc to cubic Bezier segments and records them in p. + * + * @param p The target for the cubic Bezier segments + * @param cx The x coordinate center of the ellipse + * @param cy The y coordinate center of the ellipse + * @param a The radius of the ellipse in the horizontal direction + * @param b The radius of the ellipse in the vertical direction + * @param e1x E(eta1) x coordinate of the starting point of the arc + * @param e1y E(eta2) y coordinate of the starting point of the arc + * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane + * @param start The start angle of the arc on the ellipse + * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse + */ + private static void arcToBezier(Path p, + double cx, + double cy, + double a, + double b, + double e1x, + double e1y, + double theta, + double start, + double sweep) { + // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html + // and http://www.spaceroots.org/documents/ellipse/node22.html + + // Maximum of 45 degrees per cubic Bezier segment + int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI)); + + double eta1 = start; + double cosTheta = Math.cos(theta); + double sinTheta = Math.sin(theta); + double cosEta1 = Math.cos(eta1); + double sinEta1 = Math.sin(eta1); + double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1); + double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1); + + double anglePerSegment = sweep / numSegments; + for (int i = 0; i < numSegments; i++) { + double eta2 = eta1 + anglePerSegment; + double sinEta2 = Math.sin(eta2); + double cosEta2 = Math.cos(eta2); + double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2); + double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2); + double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2; + double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2; + double tanDiff2 = Math.tan((eta2 - eta1) / 2); + double alpha = + Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3; + double q1x = e1x + alpha * ep1x; + double q1y = e1y + alpha * ep1y; + double q2x = e2x - alpha * ep2x; + double q2y = e2y - alpha * ep2y; + + p.cubicTo((float) q1x, + (float) q1y, + (float) q2x, + (float) q2y, + (float) e2x, + (float) e2y); + eta1 = eta2; + e1x = e2x; + e1y = e2y; + ep1x = ep2x; + ep1y = ep2y; + } + } + } +} diff --git a/library/src/main/java/com/telly/mrvector/Utils.java b/library/src/main/java/com/telly/mrvector/Utils.java new file mode 100644 index 0000000..159464d --- /dev/null +++ b/library/src/main/java/com/telly/mrvector/Utils.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) Telly, Inc. and other contributors. + * + * 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.telly.mrvector; + +import android.annotation.TargetApi; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; +import android.util.Log; + +import static android.graphics.PorterDuff.Mode; +import static android.graphics.PorterDuff.Mode.SRC_IN; +import static android.os.Build.VERSION.SDK_INT; +import static android.os.Build.VERSION_CODES.LOLLIPOP; +import static android.util.LayoutDirection.LTR; +import static com.telly.mrvector.VectorDrawable.LOGTAG; + +/** + * @hide + */ +public class Utils { + /** + * Stolen from Drawable + */ + static final Mode DEFAULT_TINT_MODE = SRC_IN; + static final boolean LOLLIPOP_PLUS = SDK_INT >= LOLLIPOP; + + public static T tryInvoke(Object target, String methodName, Object... args) { + final int argsCount = args == null ? 0 : args.length; + Class[] argTypes = new Class[argsCount]; + for (int i = 0; i < argsCount; i++) { + argTypes[i] = args[i].getClass(); + } + + return tryInvoke(target, methodName, argTypes, args); + } + + public static T tryInvoke(Object target, String methodName, Class[] argTypes, + Object... args) { + try { + return (T) target.getClass().getDeclaredMethod(methodName, argTypes).invoke(target, args); + } catch (Exception pokemon) { + Log.e(LOGTAG, "Unable to invoke " + methodName + " on " + target, pokemon); + } + + return null; + } + + static int getLayoutDirection(Drawable drawable) { + final Integer layoutDirection = tryInvoke(drawable, "getLayoutDirection", (Object[])null); + return layoutDirection == null ? LTR : layoutDirection.intValue(); + } + + /** + * Parses a {@link Mode} from a tintMode + * attribute's enum value. + * + * @hide + */ + static Mode parseTintMode(int value, Mode defaultMode) { + switch (value) { + case 3: return Mode.SRC_OVER; + case 5: return Mode.SRC_IN; + case 9: return Mode.SRC_ATOP; + case 14: return Mode.MULTIPLY; + case 15: return Mode.SCREEN; + case 16: return Mode.ADD; + default: return defaultMode; + } + } + + /** + * Ensures the tint filter is consistent with the current tint color and + * mode. + */ + static PorterDuffColorFilter updateTintFilter(Drawable drawable, PorterDuffColorFilter tintFilter, ColorStateList tint, + PorterDuff.Mode tintMode) { + if (tint == null || tintMode == null) { + return null; + } + + final int color = tint.getColorForState(drawable.getState(), Color.TRANSPARENT); + if (tintFilter == null || !LOLLIPOP_PLUS) { // TODO worth caching them? + return new PorterDuffColorFilter(color, tintMode); + } + + tryInvoke(tintFilter, "setColor", color); + tryInvoke(tintFilter, "setMode", tintMode); + return tintFilter; + } + + @TargetApi(LOLLIPOP) + static int getChangingConfigurations(TypedArray a) { + if (LOLLIPOP_PLUS) { + return a.getChangingConfigurations(); + } + return 0; + } +} diff --git a/library/src/main/java/com/telly/mrvector/VectorDrawable.java b/library/src/main/java/com/telly/mrvector/VectorDrawable.java new file mode 100644 index 0000000..039d853 --- /dev/null +++ b/library/src/main/java/com/telly/mrvector/VectorDrawable.java @@ -0,0 +1,1588 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * 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.telly.mrvector; + +import android.annotation.TargetApi; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff.Mode; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.Region; +import android.graphics.drawable.Drawable; +import android.support.v4.util.ArrayMap; +import android.util.AttributeSet; +import android.util.Log; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Stack; + +import static android.os.Build.VERSION_CODES.LOLLIPOP; +import static android.util.LayoutDirection.RTL; +import static com.telly.mrvector.Utils.DEFAULT_TINT_MODE; +import static com.telly.mrvector.Utils.LOLLIPOP_PLUS; +import static com.telly.mrvector.Utils.updateTintFilter; + +/** + * This lets you create a drawable based on an XML vector graphic. It can be + * defined in an XML file with the <vector> element. + *

+ * The vector drawable has the following elements: + *

+ *

<vector>
+ *
+ *
Used to defined a vector drawable + *
+ *
android:name
+ *
Defines the name of this vector drawable.
+ *
android:width
+ *
Used to defined the intrinsic width of the drawable. + * This support all the dimension units, normally specified with dp.
+ *
android:height
+ *
Used to defined the intrinsic height the drawable. + * This support all the dimension units, normally specified with dp.
+ *
android:viewportWidth
+ *
Used to defined the width of the viewport space. Viewport is basically + * the virtual canvas where the paths are drawn on.
+ *
android:viewportHeight
+ *
Used to defined the height of the viewport space. Viewport is basically + * the virtual canvas where the paths are drawn on.
+ *
android:tint
+ *
The color to apply to the drawable as a tint. By default, no tint is applied.
+ *
android:tintMode
+ *
The Porter-Duff blending mode for the tint color. The default value is src_in.
+ *
android:autoMirrored
+ *
Indicates if the drawable needs to be mirrored when its layout direction is + * RTL (right-to-left).
+ *
android:alpha
+ *
The opacity of this drawable.
+ *
+ *
+ * + *
+ *
<group>
+ *
Defines a group of paths or subgroups, plus transformation information. + * The transformations are defined in the same coordinates as the viewport. + * And the transformations are applied in the order of scale, rotate then translate. + *
+ *
android:name
+ *
Defines the name of the group.
+ *
android:rotation
+ *
The degrees of rotation of the group.
+ *
android:pivotX
+ *
The X coordinate of the pivot for the scale and rotation of the group. + * This is defined in the viewport space.
+ *
android:pivotY
+ *
The Y coordinate of the pivot for the scale and rotation of the group. + * This is defined in the viewport space.
+ *
android:scaleX
+ *
The amount of scale on the X Coordinate.
+ *
android:scaleY
+ *
The amount of scale on the Y coordinate.
+ *
android:translateX
+ *
The amount of translation on the X coordinate. + * This is defined in the viewport space.
+ *
android:translateY
+ *
The amount of translation on the Y coordinate. + * This is defined in the viewport space.
+ *
+ *
+ * + *
+ *
<path>
+ *
Defines paths to be drawn. + *
+ *
android:name
+ *
Defines the name of the path.
+ *
android:pathData
+ *
Defines path string. This is using exactly same format as "d" attribute + * in the SVG's path data. This is defined in the viewport space.
+ *
android:fillColor
+ *
Defines the color to fill the path (none if not present).
+ *
android:strokeColor
+ *
Defines the color to draw the path outline (none if not present).
+ *
android:strokeWidth
+ *
The width a path stroke.
+ *
android:strokeAlpha
+ *
The opacity of a path stroke.
+ *
android:fillAlpha
+ *
The opacity to fill the path with.
+ *
android:trimPathStart
+ *
The fraction of the path to trim from the start, in the range from 0 to 1.
+ *
android:trimPathEnd
+ *
The fraction of the path to trim from the end, in the range from 0 to 1.
+ *
android:trimPathOffset
+ *
Shift trim region (allows showed region to include the start and end), in the range + * from 0 to 1.
+ *
android:strokeLineCap
+ *
Sets the linecap for a stroked path: butt, round, square.
+ *
android:strokeLineJoin
+ *
Sets the lineJoin for a stroked path: miter,round,bevel.
+ *
android:strokeMiterLimit
+ *
Sets the Miter limit for a stroked path.
+ *
+ *
+ * + *
+ *
<clip-path>
+ *
Defines path to be the current clip. + *
+ *
android:name
+ *
Defines the name of the clip path.
+ *
android:pathData
+ *
Defines clip path string. This is using exactly same format as "d" attribute + * in the SVG's path data.
+ *
+ *
+ *
  • Here is a simple VectorDrawable in this vectordrawable.xml file. + *
    + * <vector xmlns:android="http://schemas.android.com/apk/res/android"
    + *     android:height="64dp"
    + *     android:width="64dp"
    + *     android:viewportHeight="600"
    + *     android:viewportWidth="600" >
    + *     <group
    + *         android:name="rotationGroup"
    + *         android:pivotX="300.0"
    + *         android:pivotY="300.0"
    + *         android:rotation="45.0" >
    + *         <path
    + *             android:name="v"
    + *             android:fillColor="#000000"
    + *             android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
    + *     </group>
    + * </vector>
    + * 
  • + */ + +public class VectorDrawable extends Drawable { + static final String LOGTAG = VectorDrawable.class.getSimpleName(); + + private static final String SHAPE_CLIP_PATH = "clip-path"; + private static final String SHAPE_GROUP = "group"; + private static final String SHAPE_PATH = "path"; + private static final String SHAPE_VECTOR = "vector"; + + private static final int LINECAP_BUTT = 0; + private static final int LINECAP_ROUND = 1; + private static final int LINECAP_SQUARE = 2; + + private static final int LINEJOIN_MITER = 0; + private static final int LINEJOIN_ROUND = 1; + private static final int LINEJOIN_BEVEL = 2; + + private static final boolean DBG_VECTOR_DRAWABLE = false; + + private VectorDrawableState mVectorState; + + private PorterDuffColorFilter mTintFilter; + private ColorFilter mColorFilter; + + private boolean mMutated; + + // AnimatedVectorDrawable needs to turn off the cache all the time, otherwise, + // caching the bitmap by default is allowed. + private boolean mAllowCaching = true; + + public VectorDrawable() { + mVectorState = new VectorDrawableState(); + } + + private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) { + if (theme != null && state.canApplyThemeCompat()) { + // If we need to apply a theme, implicitly mutate. + mVectorState = new VectorDrawableState(state); + applyTheme(theme); + } else { + mVectorState = state; + } + + mTintFilter = updateTintFilter(this, mTintFilter, state.mTint, state.mTintMode); + } + + @Override + public Drawable mutate() { + if (!mMutated && super.mutate() == this) { + mVectorState = new VectorDrawableState(mVectorState); + mMutated = true; + } + return this; + } + + Object getTargetByName(String name) { + return mVectorState.mVPathRenderer.mVGTargetsMap.get(name); + } + + @Override + public ConstantState getConstantState() { + mVectorState.mChangingConfigurations = getChangingConfigurations(); + return mVectorState; + } + + @Override + public void draw(Canvas canvas) { + final Rect bounds = getBounds(); + if (bounds.width() == 0 || bounds.height() == 0) { + // too small to draw + return; + } + + final int saveCount = canvas.save(); + final boolean needMirroring = needMirroring(); + + canvas.translate(bounds.left, bounds.top); + if (needMirroring) { + canvas.translate(bounds.width(), 0); + canvas.scale(-1.0f, 1.0f); + } + + // Color filters always override tint filters. + final ColorFilter colorFilter = mColorFilter == null ? mTintFilter : mColorFilter; + + if (!mAllowCaching) { + // AnimatedVectorDrawable + if (!mVectorState.hasTranslucentRoot()) { + mVectorState.mVPathRenderer.draw( + canvas, bounds.width(), bounds.height(), colorFilter); + } else { + mVectorState.createCachedBitmapIfNeeded(bounds); + mVectorState.updateCachedBitmap(bounds); + mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter); + } + } else { + // Static Vector Drawable case. + mVectorState.createCachedBitmapIfNeeded(bounds); + if (!mVectorState.canReuseCache()) { + mVectorState.updateCachedBitmap(bounds); + mVectorState.updateCacheStates(); + } + mVectorState.drawCachedBitmapWithRootAlpha(canvas, colorFilter); + } + + canvas.restoreToCount(saveCount); + } + + @Override + public int getAlpha() { + return mVectorState.mVPathRenderer.getRootAlpha(); + } + + @Override + public void setAlpha(int alpha) { + if (mVectorState.mVPathRenderer.getRootAlpha() != alpha) { + mVectorState.mVPathRenderer.setRootAlpha(alpha); + invalidateSelf(); + } + } + + @Override + public void setColorFilter(ColorFilter colorFilter) { + mColorFilter = colorFilter; + invalidateSelf(); + } + + @Override + public void setTintList(ColorStateList tint) { + final VectorDrawableState state = mVectorState; + if (state.mTint != tint) { + state.mTint = tint; + mTintFilter = updateTintFilter(this, mTintFilter, tint, state.mTintMode); + invalidateSelf(); + } + } + + @Override + public void setTintMode(Mode tintMode) { + final VectorDrawableState state = mVectorState; + if (state.mTintMode != tintMode) { + state.mTintMode = tintMode; + mTintFilter = updateTintFilter(this, mTintFilter, state.mTint, tintMode); + invalidateSelf(); + } + } + + @Override + public boolean isStateful() { + return super.isStateful() || (mVectorState != null && mVectorState.mTint != null + && mVectorState.mTint.isStateful()); + } + + @Override + protected boolean onStateChange(int[] stateSet) { + final VectorDrawableState state = mVectorState; + if (state.mTint != null && state.mTintMode != null) { + mTintFilter = updateTintFilter(this, mTintFilter, state.mTint, state.mTintMode); + invalidateSelf(); + return true; + } + return false; + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public int getIntrinsicWidth() { + return (int) mVectorState.mVPathRenderer.mBaseWidth; + } + + @Override + public int getIntrinsicHeight() { + return (int) mVectorState.mVPathRenderer.mBaseHeight; + } + + @Override + @TargetApi(LOLLIPOP) + public boolean canApplyTheme() { + // TODO THEME Not supported yet + if (!LOLLIPOP_PLUS) { + return false; + } + return super.canApplyTheme() || mVectorState != null && mVectorState.canApplyTheme(); + } + + @Override + @TargetApi(LOLLIPOP) + public void applyTheme(Theme t) { + if (LOLLIPOP_PLUS) { + super.applyTheme(t); + } + + final VectorDrawableState state = mVectorState; + if (state != null && state.mThemeAttrs != null) { + /* TODO THEME Not supported yet + final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.VectorDrawable); + try { + state.mCacheDirty = true; + updateStateFromTypedArray(a); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } finally { + a.recycle(); + } + */ + + mTintFilter = updateTintFilter(this, mTintFilter, state.mTint, state.mTintMode); + } + + final VPathRenderer path = state.mVPathRenderer; + if (path != null && path.canApplyTheme()) { + path.applyTheme(t); + } + } + + /** + * The size of a pixel when scaled from the intrinsic dimension to the viewport dimension. + * This is used to calculate the path animation accuracy. + * + * @hide + */ + public float getPixelSize() { + if (mVectorState == null && mVectorState.mVPathRenderer == null || + mVectorState.mVPathRenderer.mBaseWidth == 0 || + mVectorState.mVPathRenderer.mBaseHeight == 0 || + mVectorState.mVPathRenderer.mViewportHeight == 0 || + mVectorState.mVPathRenderer.mViewportWidth == 0) { + return 1; // fall back to 1:1 pixel mapping. + } + float intrinsicWidth = mVectorState.mVPathRenderer.mBaseWidth; + float intrinsicHeight = mVectorState.mVPathRenderer.mBaseHeight; + float viewportWidth = mVectorState.mVPathRenderer.mViewportWidth; + float viewportHeight = mVectorState.mVPathRenderer.mViewportHeight; + float scaleX = viewportWidth / intrinsicWidth; + float scaleY = viewportHeight / intrinsicHeight; + return Math.min(scaleX, scaleY); + } + + /** @hide */ + static VectorDrawable create(Resources resources, int rid) { + try { + final XmlPullParser parser = resources.getXml(rid); + final AttributeSet attrs = Xml.asAttributeSet(parser); + int type; + while ((type=parser.next()) != XmlPullParser.START_TAG && + type != XmlPullParser.END_DOCUMENT) { + // Empty loop + } + if (type != XmlPullParser.START_TAG) { + throw new XmlPullParserException("No start tag found"); + } + + final VectorDrawable drawable = new VectorDrawable(); + drawable.inflate(resources, parser, attrs); + + return drawable; + } catch (XmlPullParserException e) { + Log.e(LOGTAG, "parser error", e); + } catch (IOException e) { + Log.e(LOGTAG, "parser error", e); + } + return null; + } + + private static int applyAlpha(int color, float alpha) { + int alphaBytes = Color.alpha(color); + color &= 0x00FFFFFF; + color |= ((int) (alphaBytes * alpha)) << 24; + return color; + } + + @Override + public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs) + throws XmlPullParserException, IOException { + // TODO THEME Not supported yet + Theme theme = null; + final VectorDrawableState state = mVectorState; + final VPathRenderer pathRenderer = new VPathRenderer(); + state.mVPathRenderer = pathRenderer; + + final TypedArray a = res.obtainAttributes(attrs, R.styleable.VectorDrawable); + updateStateFromTypedArray(a); + a.recycle(); + + state.mCacheDirty = true; + inflateInternal(res, parser, attrs, theme); + + mTintFilter = updateTintFilter(this, mTintFilter, state.mTint, state.mTintMode); + } + + private void updateStateFromTypedArray(TypedArray a) throws XmlPullParserException { + final VectorDrawableState state = mVectorState; + final VPathRenderer pathRenderer = state.mVPathRenderer; + + // Account for any configuration changes. + state.mChangingConfigurations |= Utils.getChangingConfigurations(a); + + // Extract the theme attributes, if any. + state.mThemeAttrs = null; // TODO THEME TINT Not supported yet a.extractThemeAttrs(); + + final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1); + if (tintMode != -1) { + state.mTintMode = Utils.parseTintMode(tintMode, DEFAULT_TINT_MODE); + } + + final ColorStateList tint = a.getColorStateList(R.styleable.VectorDrawable_tint); + if (tint != null) { + state.mTint = tint; + } + + state.mAutoMirrored = a.getBoolean( + R.styleable.VectorDrawable_autoMirrored, state.mAutoMirrored); + + pathRenderer.mViewportWidth = a.getFloat( + R.styleable.VectorDrawable_viewportWidth, pathRenderer.mViewportWidth); + pathRenderer.mViewportHeight = a.getFloat( + R.styleable.VectorDrawable_viewportHeight, pathRenderer.mViewportHeight); + + if (pathRenderer.mViewportWidth <= 0) { + throw new XmlPullParserException(a.getPositionDescription() + + " tag requires viewportWidth > 0"); + } else if (pathRenderer.mViewportHeight <= 0) { + throw new XmlPullParserException(a.getPositionDescription() + + " tag requires viewportHeight > 0"); + } + + pathRenderer.mBaseWidth = a.getDimension( + R.styleable.VectorDrawable_width, pathRenderer.mBaseWidth); + pathRenderer.mBaseHeight = a.getDimension( + R.styleable.VectorDrawable_height, pathRenderer.mBaseHeight); + + if (pathRenderer.mBaseWidth <= 0) { + throw new XmlPullParserException(a.getPositionDescription() + + " tag requires width > 0"); + } else if (pathRenderer.mBaseHeight <= 0) { + throw new XmlPullParserException(a.getPositionDescription() + + " tag requires height > 0"); + } + + final float alphaInFloat = a.getFloat(R.styleable.VectorDrawable_alpha, + pathRenderer.getAlpha()); + pathRenderer.setAlpha(alphaInFloat); + + final String name = a.getString(R.styleable.VectorDrawable_name); + if (name != null) { + pathRenderer.mRootName = name; + pathRenderer.mVGTargetsMap.put(name, pathRenderer); + } + } + + private void inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, + Theme theme) throws XmlPullParserException, IOException { + final VectorDrawableState state = mVectorState; + final VPathRenderer pathRenderer = state.mVPathRenderer; + boolean noPathTag = true; + + // Use a stack to help to build the group tree. + // The top of the stack is always the current group. + final Stack groupStack = new Stack(); + groupStack.push(pathRenderer.mRootGroup); + + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + final String tagName = parser.getName(); + final VGroup currentGroup = groupStack.peek(); + + if (SHAPE_PATH.equals(tagName)) { + final VFullPath path = new VFullPath(); + path.inflate(res, attrs, theme); + currentGroup.mChildren.add(path); + if (path.getPathName() != null) { + pathRenderer.mVGTargetsMap.put(path.getPathName(), path); + } + noPathTag = false; + state.mChangingConfigurations |= path.mChangingConfigurations; + } else if (SHAPE_CLIP_PATH.equals(tagName)) { + final VClipPath path = new VClipPath(); + path.inflate(res, attrs, theme); + currentGroup.mChildren.add(path); + if (path.getPathName() != null) { + pathRenderer.mVGTargetsMap.put(path.getPathName(), path); + } + state.mChangingConfigurations |= path.mChangingConfigurations; + } else if (SHAPE_GROUP.equals(tagName)) { + VGroup newChildGroup = new VGroup(); + newChildGroup.inflate(res, attrs, theme); + currentGroup.mChildren.add(newChildGroup); + groupStack.push(newChildGroup); + if (newChildGroup.getGroupName() != null) { + pathRenderer.mVGTargetsMap.put(newChildGroup.getGroupName(), + newChildGroup); + } + state.mChangingConfigurations |= newChildGroup.mChangingConfigurations; + } + } else if (eventType == XmlPullParser.END_TAG) { + final String tagName = parser.getName(); + if (SHAPE_GROUP.equals(tagName)) { + groupStack.pop(); + } + } + eventType = parser.next(); + } + + // Print the tree out for debug. + if (DBG_VECTOR_DRAWABLE) { + printGroupTree(pathRenderer.mRootGroup, 0); + } + + if (noPathTag) { + final StringBuffer tag = new StringBuffer(); + + if (tag.length() > 0) { + tag.append(" or "); + } + tag.append(SHAPE_PATH); + + throw new XmlPullParserException("no " + tag + " defined"); + } + } + + private void printGroupTree(VGroup currentGroup, int level) { + String indent = ""; + for (int i = 0; i < level; i++) { + indent += " "; + } + // Print the current node + Log.v(LOGTAG, indent + "current group is :" + currentGroup.getGroupName() + + " rotation is " + currentGroup.mRotate); + Log.v(LOGTAG, indent + "matrix is :" + currentGroup.getLocalMatrix().toString()); + // Then print all the children groups + for (int i = 0; i < currentGroup.mChildren.size(); i++) { + Object child = currentGroup.mChildren.get(i); + if (child instanceof VGroup) { + printGroupTree((VGroup) child, level + 1); + } + } + } + + @Override + public int getChangingConfigurations() { + return super.getChangingConfigurations() | mVectorState.mChangingConfigurations; + } + + void setAllowCaching(boolean allowCaching) { + mAllowCaching = allowCaching; + } + + private boolean needMirroring() { + return isAutoMirrored() && Utils.getLayoutDirection(this) == RTL; + } + + @Override + public void setAutoMirrored(boolean mirrored) { + if (mVectorState.mAutoMirrored != mirrored) { + mVectorState.mAutoMirrored = mirrored; + invalidateSelf(); + } + } + + @Override + public boolean isAutoMirrored() { + return mVectorState.mAutoMirrored; + } + + private static class VectorDrawableState extends ConstantState { + int[] mThemeAttrs; + int mChangingConfigurations; + VPathRenderer mVPathRenderer; + ColorStateList mTint = null; + Mode mTintMode = DEFAULT_TINT_MODE; + boolean mAutoMirrored; + + Bitmap mCachedBitmap; + int[] mCachedThemeAttrs; + ColorStateList mCachedTint; + Mode mCachedTintMode; + int mCachedRootAlpha; + boolean mCachedAutoMirrored; + boolean mCacheDirty; + + /** Temporary paint object used to draw cached bitmaps. */ + Paint mTempPaint; + + // Deep copy for mutate() or implicitly mutate. + public VectorDrawableState(VectorDrawableState copy) { + if (copy != null) { + mThemeAttrs = copy.mThemeAttrs; + mChangingConfigurations = copy.mChangingConfigurations; + mVPathRenderer = new VPathRenderer(copy.mVPathRenderer); + if (copy.mVPathRenderer.mFillPaint != null) { + mVPathRenderer.mFillPaint = new Paint(copy.mVPathRenderer.mFillPaint); + } + if (copy.mVPathRenderer.mStrokePaint != null) { + mVPathRenderer.mStrokePaint = new Paint(copy.mVPathRenderer.mStrokePaint); + } + mTint = copy.mTint; + mTintMode = copy.mTintMode; + mAutoMirrored = copy.mAutoMirrored; + } + } + + public void drawCachedBitmapWithRootAlpha(Canvas canvas, ColorFilter filter) { + // The bitmap's size is the same as the bounds. + final Paint p = getPaint(filter); + canvas.drawBitmap(mCachedBitmap, 0, 0, p); + } + + public boolean hasTranslucentRoot() { + return mVPathRenderer.getRootAlpha() < 255; + } + + /** + * @return null when there is no need for alpha paint. + */ + public Paint getPaint(ColorFilter filter) { + if (!hasTranslucentRoot() && filter == null) { + return null; + } + + if (mTempPaint == null) { + mTempPaint = new Paint(); + mTempPaint.setFilterBitmap(true); + } + mTempPaint.setAlpha(mVPathRenderer.getRootAlpha()); + mTempPaint.setColorFilter(filter); + return mTempPaint; + } + + public void updateCachedBitmap(Rect bounds) { + mCachedBitmap.eraseColor(Color.TRANSPARENT); + Canvas tmpCanvas = new Canvas(mCachedBitmap); + mVPathRenderer.draw(tmpCanvas, bounds.width(), bounds.height(), null); + } + + public void createCachedBitmapIfNeeded(Rect bounds) { + if (mCachedBitmap == null || !canReuseBitmap(bounds.width(), + bounds.height())) { + mCachedBitmap = Bitmap.createBitmap(bounds.width(), bounds.height(), + Bitmap.Config.ARGB_8888); + mCacheDirty = true; + } + + } + + public boolean canReuseBitmap(int width, int height) { + if (width == mCachedBitmap.getWidth() + && height == mCachedBitmap.getHeight()) { + return true; + } + return false; + } + + public boolean canReuseCache() { + if (!mCacheDirty + && mCachedThemeAttrs == mThemeAttrs + && mCachedTint == mTint + && mCachedTintMode == mTintMode + && mCachedAutoMirrored == mAutoMirrored + && mCachedRootAlpha == mVPathRenderer.getRootAlpha()) { + return true; + } + return false; + } + + public void updateCacheStates() { + // Use shallow copy here and shallow comparison in canReuseCache(), + // likely hit cache miss more, but practically not much difference. + mCachedThemeAttrs = mThemeAttrs; + mCachedTint = mTint; + mCachedTintMode = mTintMode; + mCachedRootAlpha = mVPathRenderer.getRootAlpha(); + mCachedAutoMirrored = mAutoMirrored; + mCacheDirty = false; + } + + @Override + @TargetApi(LOLLIPOP) + public boolean canApplyTheme() { + if (!LOLLIPOP_PLUS) { + return false; + } + return super.canApplyTheme() || mThemeAttrs != null + || (mVPathRenderer != null && mVPathRenderer.canApplyTheme()); + } + + boolean canApplyThemeCompat() { + return canApplyTheme(); + } + + public VectorDrawableState() { + mVPathRenderer = new VPathRenderer(); + } + + @Override + public Drawable newDrawable() { + return new VectorDrawable(this, null, null); + } + + @Override + public Drawable newDrawable(Resources res) { + return new VectorDrawable(this, res, null); + } + + @Override + @TargetApi(LOLLIPOP) + public Drawable newDrawable(Resources res, Theme theme) { + return new VectorDrawable(this, res, theme); + } + + @Override + public int getChangingConfigurations() { + return mChangingConfigurations; + } + } + + private static class VPathRenderer { + /* Right now the internal data structure is organized as a tree. + * Each node can be a group node, or a path. + * A group node can have groups or paths as children, but a path node has + * no children. + * One example can be: + * Root Group + * / | \ + * Group Path Group + * / \ | + * Path Path Path + * + */ + // Variables that only used temporarily inside the draw() call, so there + // is no need for deep copying. + private final Path mPath; + private final Path mRenderPath; + private static final Matrix IDENTITY_MATRIX = new Matrix(); + private final Matrix mFinalPathMatrix = new Matrix(); + + private Paint mStrokePaint; + private Paint mFillPaint; + private PathMeasure mPathMeasure; + + ///////////////////////////////////////////////////// + // Variables below need to be copied (deep copy if applicable) for mutation. + private int mChangingConfigurations; + private final VGroup mRootGroup; + float mBaseWidth = 0; + float mBaseHeight = 0; + float mViewportWidth = 0; + float mViewportHeight = 0; + int mRootAlpha = 0xFF; + String mRootName = null; + + final ArrayMap mVGTargetsMap = new ArrayMap(); + + public VPathRenderer() { + mRootGroup = new VGroup(); + mPath = new Path(); + mRenderPath = new Path(); + } + + public void setRootAlpha(int alpha) { + mRootAlpha = alpha; + } + + public int getRootAlpha() { + return mRootAlpha; + } + + // setAlpha() and getAlpha() are used mostly for animation purpose, since + // Animator like to use alpha from 0 to 1. + public void setAlpha(float alpha) { + setRootAlpha((int) (alpha * 255)); + } + + @SuppressWarnings("unused") + public float getAlpha() { + return getRootAlpha() / 255.0f; + } + + public VPathRenderer(VPathRenderer copy) { + mRootGroup = new VGroup(copy.mRootGroup, mVGTargetsMap); + mPath = new Path(copy.mPath); + mRenderPath = new Path(copy.mRenderPath); + mBaseWidth = copy.mBaseWidth; + mBaseHeight = copy.mBaseHeight; + mViewportWidth = copy.mViewportWidth; + mViewportHeight = copy.mViewportHeight; + mChangingConfigurations = copy.mChangingConfigurations; + mRootAlpha = copy.mRootAlpha; + mRootName = copy.mRootName; + if (copy.mRootName != null) { + mVGTargetsMap.put(copy.mRootName, this); + } + } + + public boolean canApplyTheme() { + // If one of the paths can apply theme, then return true; + return recursiveCanApplyTheme(mRootGroup); + } + + private boolean recursiveCanApplyTheme(VGroup currentGroup) { + // We can do a tree traverse here, if there is one path return true, + // then we return true for the whole tree. + final ArrayList children = currentGroup.mChildren; + + for (int i = 0; i < children.size(); i++) { + Object child = children.get(i); + if (child instanceof VGroup) { + VGroup childGroup = (VGroup) child; + if (childGroup.canApplyTheme() + || recursiveCanApplyTheme(childGroup)) { + return true; + } + } else if (child instanceof VPath) { + VPath childPath = (VPath) child; + if (childPath.canApplyTheme()) { + return true; + } + } + } + return false; + } + + public void applyTheme(Theme t) { + // Apply theme to every path of the tree. + recursiveApplyTheme(mRootGroup, t); + } + + private void recursiveApplyTheme(VGroup currentGroup, Theme t) { + // We can do a tree traverse here, apply theme to all paths which + // can apply theme. + final ArrayList children = currentGroup.mChildren; + for (int i = 0; i < children.size(); i++) { + Object child = children.get(i); + if (child instanceof VGroup) { + VGroup childGroup = (VGroup) child; + if (childGroup.canApplyTheme()) { + childGroup.applyTheme(t); + } + recursiveApplyTheme(childGroup, t); + } else if (child instanceof VPath) { + VPath childPath = (VPath) child; + if (childPath.canApplyTheme()) { + childPath.applyTheme(t); + } + } + } + } + + private void drawGroupTree(VGroup currentGroup, Matrix currentMatrix, + Canvas canvas, int w, int h, ColorFilter filter) { + // Calculate current group's matrix by preConcat the parent's and + // and the current one on the top of the stack. + // Basically the Mfinal = Mviewport * M0 * M1 * M2; + // Mi the local matrix at level i of the group tree. + currentGroup.mStackedMatrix.set(currentMatrix); + + currentGroup.mStackedMatrix.preConcat(currentGroup.mLocalMatrix); + + // Draw the group tree in the same order as the XML file. + for (int i = 0; i < currentGroup.mChildren.size(); i++) { + Object child = currentGroup.mChildren.get(i); + if (child instanceof VGroup) { + VGroup childGroup = (VGroup) child; + drawGroupTree(childGroup, currentGroup.mStackedMatrix, + canvas, w, h, filter); + } else if (child instanceof VPath) { + VPath childPath = (VPath) child; + drawPath(currentGroup, childPath, canvas, w, h, filter); + } + } + } + + public void draw(Canvas canvas, int w, int h, ColorFilter filter) { + // Travese the tree in pre-order to draw. + drawGroupTree(mRootGroup, IDENTITY_MATRIX, canvas, w, h, filter); + } + + private void drawPath(VGroup vGroup, VPath vPath, Canvas canvas, int w, int h, + ColorFilter filter) { + final float scaleX = w / mViewportWidth; + final float scaleY = h / mViewportHeight; + final float minScale = Math.min(scaleX, scaleY); + + mFinalPathMatrix.set(vGroup.mStackedMatrix); + mFinalPathMatrix.postScale(scaleX, scaleY); + + vPath.toPath(mPath); + final Path path = mPath; + + mRenderPath.reset(); + + if (vPath.isClipPath()) { + mRenderPath.addPath(path, mFinalPathMatrix); + canvas.clipPath(mRenderPath, Region.Op.REPLACE); + } else { + VFullPath fullPath = (VFullPath) vPath; + if (fullPath.mTrimPathStart != 0.0f || fullPath.mTrimPathEnd != 1.0f) { + float start = (fullPath.mTrimPathStart + fullPath.mTrimPathOffset) % 1.0f; + float end = (fullPath.mTrimPathEnd + fullPath.mTrimPathOffset) % 1.0f; + + if (mPathMeasure == null) { + mPathMeasure = new PathMeasure(); + } + mPathMeasure.setPath(mPath, false); + + float len = mPathMeasure.getLength(); + start = start * len; + end = end * len; + path.reset(); + if (start > end) { + mPathMeasure.getSegment(start, len, path, true); + mPathMeasure.getSegment(0f, end, path, true); + } else { + mPathMeasure.getSegment(start, end, path, true); + } + path.rLineTo(0, 0); // fix bug in measure + } + mRenderPath.addPath(path, mFinalPathMatrix); + + if (fullPath.mFillColor != Color.TRANSPARENT) { + if (mFillPaint == null) { + mFillPaint = new Paint(); + mFillPaint.setStyle(Paint.Style.FILL); + mFillPaint.setAntiAlias(true); + } + + final Paint fillPaint = mFillPaint; + fillPaint.setColor(applyAlpha(fullPath.mFillColor, fullPath.mFillAlpha)); + fillPaint.setColorFilter(filter); + canvas.drawPath(mRenderPath, fillPaint); + } + + if (fullPath.mStrokeColor != Color.TRANSPARENT) { + if (mStrokePaint == null) { + mStrokePaint = new Paint(); + mStrokePaint.setStyle(Paint.Style.STROKE); + mStrokePaint.setAntiAlias(true); + } + + final Paint strokePaint = mStrokePaint; + if (fullPath.mStrokeLineJoin != null) { + strokePaint.setStrokeJoin(fullPath.mStrokeLineJoin); + } + + if (fullPath.mStrokeLineCap != null) { + strokePaint.setStrokeCap(fullPath.mStrokeLineCap); + } + + strokePaint.setStrokeMiter(fullPath.mStrokeMiterlimit); + strokePaint.setColor(applyAlpha(fullPath.mStrokeColor, fullPath.mStrokeAlpha)); + strokePaint.setColorFilter(filter); + strokePaint.setStrokeWidth(fullPath.mStrokeWidth * minScale); + canvas.drawPath(mRenderPath, strokePaint); + } + } + } + } + + private static class VGroup { + // mStackedMatrix is only used temporarily when drawing, it combines all + // the parents' local matrices with the current one. + private final Matrix mStackedMatrix = new Matrix(); + + ///////////////////////////////////////////////////// + // Variables below need to be copied (deep copy if applicable) for mutation. + final ArrayList mChildren = new ArrayList(); + + private float mRotate = 0; + private float mPivotX = 0; + private float mPivotY = 0; + private float mScaleX = 1; + private float mScaleY = 1; + private float mTranslateX = 0; + private float mTranslateY = 0; + + // mLocalMatrix is updated based on the update of transformation information, + // either parsed from the XML or by animation. + private final Matrix mLocalMatrix = new Matrix(); + private int mChangingConfigurations; + private int[] mThemeAttrs; + private String mGroupName = null; + + public VGroup(VGroup copy, ArrayMap targetsMap) { + mRotate = copy.mRotate; + mPivotX = copy.mPivotX; + mPivotY = copy.mPivotY; + mScaleX = copy.mScaleX; + mScaleY = copy.mScaleY; + mTranslateX = copy.mTranslateX; + mTranslateY = copy.mTranslateY; + mThemeAttrs = copy.mThemeAttrs; + mGroupName = copy.mGroupName; + mChangingConfigurations = copy.mChangingConfigurations; + if (mGroupName != null) { + targetsMap.put(mGroupName, this); + } + + mLocalMatrix.set(copy.mLocalMatrix); + + final ArrayList children = copy.mChildren; + for (int i = 0; i < children.size(); i++) { + Object copyChild = children.get(i); + if (copyChild instanceof VGroup) { + VGroup copyGroup = (VGroup) copyChild; + mChildren.add(new VGroup(copyGroup, targetsMap)); + } else { + VPath newPath = null; + if (copyChild instanceof VFullPath) { + newPath = new VFullPath((VFullPath) copyChild); + } else if (copyChild instanceof VClipPath) { + newPath = new VClipPath((VClipPath) copyChild); + } else { + throw new IllegalStateException("Unknown object in the tree!"); + } + mChildren.add(newPath); + if (newPath.mPathName != null) { + targetsMap.put(newPath.mPathName, newPath); + } + } + } + } + + public VGroup() { + } + + public String getGroupName() { + return mGroupName; + } + + public Matrix getLocalMatrix() { + return mLocalMatrix; + } + + public void inflate(Resources res, AttributeSet attrs, Theme theme) { + // TODO TINT THEME Not supported yet + final TypedArray a = res.obtainAttributes(attrs, + R.styleable.VectorDrawableGroup); + updateStateFromTypedArray(a); + a.recycle(); + } + + private void updateStateFromTypedArray(TypedArray a) { + // Account for any configuration changes. + mChangingConfigurations |= Utils.getChangingConfigurations(a); + + // Extract the theme attributes, if any. + mThemeAttrs = null; // TODO TINT THEME Not supported yet a.extractThemeAttrs(); + + mRotate = a.getFloat(R.styleable.VectorDrawableGroup_rotation, mRotate); + mPivotX = a.getFloat(R.styleable.VectorDrawableGroup_pivotX, mPivotX); + mPivotY = a.getFloat(R.styleable.VectorDrawableGroup_pivotY, mPivotY); + mScaleX = a.getFloat(R.styleable.VectorDrawableGroup_scaleX, mScaleX); + mScaleY = a.getFloat(R.styleable.VectorDrawableGroup_scaleY, mScaleY); + mTranslateX = a.getFloat(R.styleable.VectorDrawableGroup_translateX, mTranslateX); + mTranslateY = a.getFloat(R.styleable.VectorDrawableGroup_translateY, mTranslateY); + + final String groupName = a.getString(R.styleable.VectorDrawableGroup_name); + if (groupName != null) { + mGroupName = groupName; + } + + updateLocalMatrix(); + } + + public boolean canApplyTheme() { + return mThemeAttrs != null; + } + + public void applyTheme(Theme t) { + if (mThemeAttrs == null) { + return; + } + /* TODO TINT THEME Not supported yet + final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawableGroup); + updateStateFromTypedArray(a); + a.recycle(); + */ + } + + private void updateLocalMatrix() { + // The order we apply is the same as the + // RenderNode.cpp::applyViewPropertyTransforms(). + mLocalMatrix.reset(); + mLocalMatrix.postTranslate(-mPivotX, -mPivotY); + mLocalMatrix.postScale(mScaleX, mScaleY); + mLocalMatrix.postRotate(mRotate, 0, 0); + mLocalMatrix.postTranslate(mTranslateX + mPivotX, mTranslateY + mPivotY); + } + + /* Setters and Getters, used by animator from AnimatedVectorDrawable. */ + @SuppressWarnings("unused") + public float getRotation() { + return mRotate; + } + + @SuppressWarnings("unused") + public void setRotation(float rotation) { + if (rotation != mRotate) { + mRotate = rotation; + updateLocalMatrix(); + } + } + + @SuppressWarnings("unused") + public float getPivotX() { + return mPivotX; + } + + @SuppressWarnings("unused") + public void setPivotX(float pivotX) { + if (pivotX != mPivotX) { + mPivotX = pivotX; + updateLocalMatrix(); + } + } + + @SuppressWarnings("unused") + public float getPivotY() { + return mPivotY; + } + + @SuppressWarnings("unused") + public void setPivotY(float pivotY) { + if (pivotY != mPivotY) { + mPivotY = pivotY; + updateLocalMatrix(); + } + } + + @SuppressWarnings("unused") + public float getScaleX() { + return mScaleX; + } + + @SuppressWarnings("unused") + public void setScaleX(float scaleX) { + if (scaleX != mScaleX) { + mScaleX = scaleX; + updateLocalMatrix(); + } + } + + @SuppressWarnings("unused") + public float getScaleY() { + return mScaleY; + } + + @SuppressWarnings("unused") + public void setScaleY(float scaleY) { + if (scaleY != mScaleY) { + mScaleY = scaleY; + updateLocalMatrix(); + } + } + + @SuppressWarnings("unused") + public float getTranslateX() { + return mTranslateX; + } + + @SuppressWarnings("unused") + public void setTranslateX(float translateX) { + if (translateX != mTranslateX) { + mTranslateX = translateX; + updateLocalMatrix(); + } + } + + @SuppressWarnings("unused") + public float getTranslateY() { + return mTranslateY; + } + + @SuppressWarnings("unused") + public void setTranslateY(float translateY) { + if (translateY != mTranslateY) { + mTranslateY = translateY; + updateLocalMatrix(); + } + } + } + + /** + * Common Path information for clip path and normal path. + */ + private static class VPath { + protected PathParser.PathDataNode[] mNodes = null; + String mPathName; + int mChangingConfigurations; + + public VPath() { + // Empty constructor. + } + + public VPath(VPath copy) { + mPathName = copy.mPathName; + mChangingConfigurations = copy.mChangingConfigurations; + mNodes = PathParser.deepCopyNodes(copy.mNodes); + } + + public void toPath(Path path) { + path.reset(); + if (mNodes != null) { + PathParser.PathDataNode.nodesToPath(mNodes, path); + } + } + + public String getPathName() { + return mPathName; + } + + public boolean canApplyTheme() { + return false; + } + + public void applyTheme(Theme t) { + } + + public boolean isClipPath() { + return false; + } + + /* Setters and Getters, used by animator from AnimatedVectorDrawable. */ + @SuppressWarnings("unused") + public PathParser.PathDataNode[] getPathData() { + return mNodes; + } + + @SuppressWarnings("unused") + public void setPathData(PathParser.PathDataNode[] nodes) { + if (!PathParser.canMorph(mNodes, nodes)) { + // This should not happen in the middle of animation. + mNodes = PathParser.deepCopyNodes(nodes); + } else { + PathParser.updateNodes(mNodes, nodes); + } + } + } + + /** + * Clip path, which only has name and pathData. + */ + private static class VClipPath extends VPath { + public VClipPath() { + // Empty constructor. + } + + public VClipPath(VClipPath copy) { + super(copy); + } + + public void inflate(Resources r, AttributeSet attrs, Theme theme) { + // TODO TINT THEME Not supported yet + final TypedArray a = r.obtainAttributes(attrs, + R.styleable.VectorDrawableClipPath); + updateStateFromTypedArray(a); + a.recycle(); + } + + private void updateStateFromTypedArray(TypedArray a) { + // Account for any configuration changes. + mChangingConfigurations |= Utils.getChangingConfigurations(a);; + + final String pathName = a.getString(R.styleable.VectorDrawableClipPath_name); + if (pathName != null) { + mPathName = pathName; + } + + final String pathData = a.getString(R.styleable.VectorDrawableClipPath_pathData); + if (pathData != null) { + mNodes = PathParser.createNodesFromPathData(pathData); + } + } + + @Override + public boolean isClipPath() { + return true; + } + } + + /** + * Normal path, which contains all the fill / paint information. + */ + private static class VFullPath extends VPath { + ///////////////////////////////////////////////////// + // Variables below need to be copied (deep copy if applicable) for mutation. + private int[] mThemeAttrs; + + int mStrokeColor = Color.TRANSPARENT; + float mStrokeWidth = 0; + + int mFillColor = Color.TRANSPARENT; + float mStrokeAlpha = 1.0f; + int mFillRule; + float mFillAlpha = 1.0f; + float mTrimPathStart = 0; + float mTrimPathEnd = 1; + float mTrimPathOffset = 0; + + Paint.Cap mStrokeLineCap = Paint.Cap.BUTT; + Paint.Join mStrokeLineJoin = Paint.Join.MITER; + float mStrokeMiterlimit = 4; + + public VFullPath() { + // Empty constructor. + } + + public VFullPath(VFullPath copy) { + super(copy); + mThemeAttrs = copy.mThemeAttrs; + + mStrokeColor = copy.mStrokeColor; + mStrokeWidth = copy.mStrokeWidth; + mStrokeAlpha = copy.mStrokeAlpha; + mFillColor = copy.mFillColor; + mFillRule = copy.mFillRule; + mFillAlpha = copy.mFillAlpha; + mTrimPathStart = copy.mTrimPathStart; + mTrimPathEnd = copy.mTrimPathEnd; + mTrimPathOffset = copy.mTrimPathOffset; + + mStrokeLineCap = copy.mStrokeLineCap; + mStrokeLineJoin = copy.mStrokeLineJoin; + mStrokeMiterlimit = copy.mStrokeMiterlimit; + } + + private Paint.Cap getStrokeLineCap(int id, Paint.Cap defValue) { + switch (id) { + case LINECAP_BUTT: + return Paint.Cap.BUTT; + case LINECAP_ROUND: + return Paint.Cap.ROUND; + case LINECAP_SQUARE: + return Paint.Cap.SQUARE; + default: + return defValue; + } + } + + private Paint.Join getStrokeLineJoin(int id, Paint.Join defValue) { + switch (id) { + case LINEJOIN_MITER: + return Paint.Join.MITER; + case LINEJOIN_ROUND: + return Paint.Join.ROUND; + case LINEJOIN_BEVEL: + return Paint.Join.BEVEL; + default: + return defValue; + } + } + + @Override + public boolean canApplyTheme() { + return mThemeAttrs != null; + } + + public void inflate(Resources r, AttributeSet attrs, Theme theme) { + final TypedArray a = r.obtainAttributes(attrs, + R.styleable.VectorDrawablePath); + updateStateFromTypedArray(a); + a.recycle(); + } + + private void updateStateFromTypedArray(TypedArray a) { + // Account for any configuration changes. + mChangingConfigurations |= Utils.getChangingConfigurations(a); + + // Extract the theme attributes, if any. + mThemeAttrs = null; // TODO TINT THEME Not supported yet a.extractThemeAttrs(); + + final String pathName = a.getString(R.styleable.VectorDrawablePath_name); + if (pathName != null) { + mPathName = pathName; + } + + final String pathData = a.getString(R.styleable.VectorDrawablePath_pathData); + if (pathData != null) { + mNodes = PathParser.createNodesFromPathData(pathData); + } + + mFillColor = a.getColor(R.styleable.VectorDrawablePath_fillColor, + mFillColor); + mFillAlpha = a.getFloat(R.styleable.VectorDrawablePath_fillAlpha, + mFillAlpha); + mStrokeLineCap = getStrokeLineCap(a.getInt( + R.styleable.VectorDrawablePath_strokeLineCap, -1), mStrokeLineCap); + mStrokeLineJoin = getStrokeLineJoin(a.getInt( + R.styleable.VectorDrawablePath_strokeLineJoin, -1), mStrokeLineJoin); + mStrokeMiterlimit = a.getFloat( + R.styleable.VectorDrawablePath_strokeMiterLimit, mStrokeMiterlimit); + mStrokeColor = a.getColor(R.styleable.VectorDrawablePath_strokeColor, + mStrokeColor); + mStrokeAlpha = a.getFloat(R.styleable.VectorDrawablePath_strokeAlpha, + mStrokeAlpha); + mStrokeWidth = a.getFloat(R.styleable.VectorDrawablePath_strokeWidth, + mStrokeWidth); + mTrimPathEnd = a.getFloat(R.styleable.VectorDrawablePath_trimPathEnd, + mTrimPathEnd); + mTrimPathOffset = a.getFloat( + R.styleable.VectorDrawablePath_trimPathOffset, mTrimPathOffset); + mTrimPathStart = a.getFloat( + R.styleable.VectorDrawablePath_trimPathStart, mTrimPathStart); + } + + @Override + public void applyTheme(Theme t) { + if (mThemeAttrs == null) { + return; + } + + /* TODO TINT THEME Not supported yet + final TypedArray a = t.resolveAttributes(mThemeAttrs, R.styleable.VectorDrawablePath); + updateStateFromTypedArray(a); + a.recycle(); + */ + } + + /* Setters and Getters, used by animator from AnimatedVectorDrawable. */ + @SuppressWarnings("unused") + int getStrokeColor() { + return mStrokeColor; + } + + @SuppressWarnings("unused") + void setStrokeColor(int strokeColor) { + mStrokeColor = strokeColor; + } + + @SuppressWarnings("unused") + float getStrokeWidth() { + return mStrokeWidth; + } + + @SuppressWarnings("unused") + void setStrokeWidth(float strokeWidth) { + mStrokeWidth = strokeWidth; + } + + @SuppressWarnings("unused") + float getStrokeAlpha() { + return mStrokeAlpha; + } + + @SuppressWarnings("unused") + void setStrokeAlpha(float strokeAlpha) { + mStrokeAlpha = strokeAlpha; + } + + @SuppressWarnings("unused") + int getFillColor() { + return mFillColor; + } + + @SuppressWarnings("unused") + void setFillColor(int fillColor) { + mFillColor = fillColor; + } + + @SuppressWarnings("unused") + float getFillAlpha() { + return mFillAlpha; + } + + @SuppressWarnings("unused") + void setFillAlpha(float fillAlpha) { + mFillAlpha = fillAlpha; + } + + @SuppressWarnings("unused") + float getTrimPathStart() { + return mTrimPathStart; + } + + @SuppressWarnings("unused") + void setTrimPathStart(float trimPathStart) { + mTrimPathStart = trimPathStart; + } + + @SuppressWarnings("unused") + float getTrimPathEnd() { + return mTrimPathEnd; + } + + @SuppressWarnings("unused") + void setTrimPathEnd(float trimPathEnd) { + mTrimPathEnd = trimPathEnd; + } + + @SuppressWarnings("unused") + float getTrimPathOffset() { + return mTrimPathOffset; + } + + @SuppressWarnings("unused") + void setTrimPathOffset(float trimPathOffset) { + mTrimPathOffset = trimPathOffset; + } + } +} \ No newline at end of file diff --git a/library/src/main/res/values/attrs.xml b/library/src/main/res/values/attrs.xml new file mode 100644 index 0000000..7f6aa87 --- /dev/null +++ b/library/src/main/res/values/attrs.xml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..70ee5af --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':demo', ':library' \ No newline at end of file