├── .gitignore
├── LICENSE
├── README.md
├── build.gradle
├── example-action.gif
├── example-reveal.gif
├── example.apk
├── gradle.properties
├── maven.gradle
└── src
└── main
├── AndroidManifest.xml
├── java
└── at
│ └── markushi
│ └── ui
│ ├── ActionView.java
│ ├── RevealColorView.java
│ ├── action
│ ├── Action.java
│ ├── BackAction.java
│ ├── CloseAction.java
│ ├── DrawerAction.java
│ ├── LineSegment.java
│ └── PlusAction.java
│ └── util
│ ├── BakedBezierInterpolator.java
│ └── UiHelper.java
└── res
└── values
├── attrs.xml
└── values.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
3 | .gradle
4 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Deprecated
2 | ====================
3 | This library is deprecated and no further development is taking place.
4 |
5 | # android-ui
6 | Android library for UI components.
7 | Gradle integration:
8 |
9 | ```groovy
10 | repositories {
11 | mavenCentral()
12 | }
13 |
14 | dependencies {
15 | compile 'com.github.markushi:android-ui:1.2'
16 | }
17 | ```
18 |
19 | Requires API level 14+
20 | [Download example apk](example.apk)
21 |
22 | ## Components
23 |
24 | ### ActionView
25 |
26 | A widget which can dynamically animate between defined Actions.
27 | ```xml
28 |
52 | A component which mimics parts of the circular reveal/hide animation introduced [in the Android-L preview](http://developer.android.com/preview/material/animations.html#reveal).
53 | Note: This is not a backport of the original reveal/hide effect.
54 |
55 | See [this example gist](https://gist.github.com/markushi/68ce8df77bed164b6275) on how to use it.
56 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 | dependencies {
6 | classpath 'com.android.tools.build:gradle:0.12.+'
7 | }
8 | }
9 | apply plugin: 'android-library'
10 |
11 | repositories {
12 | mavenCentral()
13 | }
14 |
15 | android {
16 | compileSdkVersion 19
17 | buildToolsVersion "20"
18 |
19 | defaultConfig {
20 | minSdkVersion 14
21 | versionName = VERSION_NAME
22 | versionCode = VERSION_CODE
23 | }
24 | }
25 |
26 | dependencies {
27 |
28 | }
29 |
30 | allprojects {
31 | version = VERSION_NAME
32 | group = GROUP
33 |
34 | repositories {
35 | mavenCentral()
36 | }
37 | }
38 |
39 | apply from: 'maven.gradle'
--------------------------------------------------------------------------------
/example-action.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/markushi/android-ui/a589fad7b74ace063c2b0e90741d43225b200a18/example-action.gif
--------------------------------------------------------------------------------
/example-reveal.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/markushi/android-ui/a589fad7b74ace063c2b0e90741d43225b200a18/example-reveal.gif
--------------------------------------------------------------------------------
/example.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/markushi/android-ui/a589fad7b74ace063c2b0e90741d43225b200a18/example.apk
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | VERSION_NAME=1.2
2 | VERSION_CODE=1
3 | GROUP=com.github.markushi
4 |
5 | POM_NAME=Android UI Library
6 | POM_ARTIFACT_ID=android-ui
7 | POM_PACKAGING=aar
8 |
9 | POM_DESCRIPTION=Android UI Library
10 | POM_URL=https://github.com/markushi/android-ui
11 | POM_SCM_URL=https://github.com/markushi/android-ui
12 | POM_SCM_CONNECTION=scm:git@github.com:markushi/android-ui.git
13 | POM_SCM_DEV_CONNECTION=scm:git@github.com:markushi/android-ui.git
14 | POM_LICENCE_NAME=The Apache Software License, Version 2.0
15 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
16 | POM_LICENCE_DIST=repo
17 | POM_DEVELOPER_ID=markushi
18 | POM_DEVELOPER_NAME=Markus Hintersteiner
19 |
--------------------------------------------------------------------------------
/maven.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven'
2 | apply plugin: 'signing'
3 |
4 | ext {
5 | version = VERSION_NAME
6 | group = GROUP
7 | }
8 |
9 | def isReleaseBuild() {
10 | return VERSION_NAME.contains("SNAPSHOT") == false
11 | }
12 |
13 | if (isReleaseBuild()) {
14 | println 'RELEASE BUILD'
15 | } else {
16 | println 'SNAPSHOT BUILD'
17 | }
18 |
19 | def getRepositoryUrl() {
20 | if (isReleaseBuild()) {
21 | return "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
22 | } else {
23 | return "https://oss.sonatype.org/content/repositories/snapshots/"
24 | }
25 | }
26 |
27 | def getRepositoryUsername() {
28 | return hasProperty('nexusUsername') ? nexusUsername : ""
29 | }
30 |
31 | def getRepositoryPassword() {
32 | return hasProperty('nexusPassword') ? nexusPassword : ""
33 | }
34 |
35 | afterEvaluate { project ->
36 | uploadArchives {
37 | repositories {
38 | mavenDeployer {
39 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
40 |
41 | pom.artifactId = POM_ARTIFACT_ID
42 |
43 | repository(url: getRepositoryUrl()) {
44 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
45 | }
46 |
47 | pom.project {
48 | name POM_NAME
49 | packaging POM_PACKAGING
50 | description POM_DESCRIPTION
51 | url POM_URL
52 |
53 | scm {
54 | url POM_SCM_URL
55 | connection POM_SCM_CONNECTION
56 | developerConnection POM_SCM_DEV_CONNECTION
57 | }
58 |
59 | licenses {
60 | license {
61 | name POM_LICENCE_NAME
62 | url POM_LICENCE_URL
63 | distribution POM_LICENCE_DIST
64 | }
65 | }
66 |
67 | developers {
68 | developer {
69 | id POM_DEVELOPER_ID
70 | name POM_DEVELOPER_NAME
71 | }
72 | }
73 | }
74 | }
75 | }
76 | }
77 |
78 | signing {
79 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
80 | sign configurations.archives
81 | }
82 |
83 | task androidJavadocs(type: Javadoc) {
84 | options {
85 | linksOffline "http://d.android.com/reference", "${android.sdkDirectory}/docs/reference"
86 | }
87 | source = android.sourceSets.main.java.sourceFiles
88 | classpath += project.files(project.android.getBootClasspath().join(File.pathSeparator))
89 | }
90 |
91 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
92 | classifier = 'javadoc'
93 | from androidJavadocs.destinationDir
94 | }
95 |
96 | task androidSourcesJar(type: Jar) {
97 | classifier = 'sources'
98 | from android.sourceSets.main.java.sourceFiles
99 | }
100 |
101 | artifacts {
102 | archives androidSourcesJar
103 | archives androidJavadocsJar
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
29 | * {@code 30 | *38 | * 39 | * In order to start a transition to another {@link at.markushi.ui.action.Action} call {@link #setAction(at.markushi.ui.action.Action)} 40 | * 41 | * @see at.markushi.ui.action.BackAction 42 | * @see at.markushi.ui.action.CloseAction 43 | * @see at.markushi.ui.action.DrawerAction 44 | * @see at.markushi.ui.action.PlusAction 45 | */ 46 | public class ActionView extends View { 47 | 48 | public static final int ROTATE_CLOCKWISE = 0; 49 | public static final int ROTATE_COUNTER_CLOCKWISE = 1; 50 | private static final int STROKE_SIZE_DIP = 2; 51 | 52 | private int color; 53 | private Action oldAction; 54 | private Action currentAction; 55 | private Action animatedAction; 56 | 57 | private float animationProgress; 58 | private float scale; 59 | private int padding; 60 | private Path path; 61 | private Paint paint; 62 | 63 | private float centerX; 64 | private float centerY; 65 | private boolean ready = false; 66 | private boolean animateWhenReady = false; 67 | private int rotation = ROTATE_CLOCKWISE; 68 | private long animationDuration; 69 | private int size; 70 | 71 | public ActionView(Context context) { 72 | this(context, null); 73 | } 74 | 75 | public ActionView(Context context, AttributeSet attrs) { 76 | this(context, attrs, 0); 77 | } 78 | 79 | public ActionView(Context context, AttributeSet attrs, int defStyleAttr) { 80 | super(context, attrs, defStyleAttr); 81 | animationDuration = getResources().getInteger(R.integer.av_animationDuration); 82 | animatedAction = new Action(new float[Action.ACTION_SIZE], null); 83 | 84 | final float strokeSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, STROKE_SIZE_DIP, context.getResources().getDisplayMetrics()); 85 | path = new Path(); 86 | paint = new Paint(Paint.ANTI_ALIAS_FLAG); 87 | paint.setColor(0xDDFFFFFF); 88 | paint.setStrokeWidth(strokeSize); 89 | paint.setStyle(Paint.Style.STROKE); 90 | 91 | if (attrs == null) { 92 | return; 93 | } 94 | final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionView); 95 | final int color = a.getColor(R.styleable.ActionView_av_color, 0xDDFFFFF); 96 | final int actionId = a.getInt(R.styleable.ActionView_av_action, 0); 97 | a.recycle(); 98 | 99 | paint.setColor(color); 100 | final Action action = getActionFromEnum(actionId); 101 | setAction(action); 102 | } 103 | 104 | @Override 105 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 106 | super.onSizeChanged(w, h, oldw, oldh); 107 | this.centerX = w / 2; 108 | this.centerY = h / 2; 109 | padding = getPaddingLeft(); 110 | size = Math.min(w, h); 111 | scale = Math.min(w, h) - (2 * padding); 112 | ready = true; 113 | transformActions(); 114 | 115 | if (animateWhenReady) { 116 | animateWhenReady = false; 117 | startAnimation(); 118 | } 119 | } 120 | 121 | @Override 122 | protected void onDraw(Canvas canvas) { 123 | super.onDraw(canvas); 124 | 125 | if (currentAction == null) { 126 | return; 127 | } 128 | 129 | if (oldAction == null) { 130 | updatePath(currentAction); 131 | } else { 132 | final float inverseProgress = 1f - animationProgress; 133 | final float[] current = currentAction.getLineData(); 134 | final float[] old = oldAction.getLineData(); 135 | final float[] animated = animatedAction.getLineData(); 136 | for (int i = 0; i < animated.length; i++) { 137 | animated[i] = current[i] * animationProgress + old[i] * inverseProgress; 138 | } 139 | updatePath(animatedAction); 140 | } 141 | 142 | canvas.rotate((rotation == ROTATE_CLOCKWISE ? 180f : -180f) * animationProgress, centerX, centerY); 143 | canvas.drawPath(path, paint); 144 | } 145 | 146 | @SuppressWarnings("UnusedDeclaration") 147 | public float getAnimationProgress() { 148 | return animationProgress; 149 | } 150 | 151 | @SuppressWarnings("UnusedDeclaration") 152 | public void setAnimationProgress(float animationProgress) { 153 | this.animationProgress = animationProgress; 154 | UiHelper.postInvalidateOnAnimation(this); 155 | } 156 | 157 | /** 158 | * Set the color used for drawing an {@link at.markushi.ui.action.Action}. 159 | * 160 | * @param color 161 | */ 162 | public void setColor(final int color) { 163 | this.color = color; 164 | paint.setColor(color); 165 | UiHelper.postInvalidateOnAnimation(this); 166 | } 167 | 168 | /** 169 | * @return the current action 170 | */ 171 | public Action getAction() { 172 | return currentAction; 173 | } 174 | 175 | /** 176 | * Sets the new action. If an action was set before a transition will be started. 177 | * 178 | * @param action 179 | * @see #setAction(at.markushi.ui.action.Action, boolean) 180 | * @see #setAction(at.markushi.ui.action.Action, boolean, int) 181 | * @see #setAction(at.markushi.ui.action.Action, at.markushi.ui.action.Action, int, long) 182 | */ 183 | public void setAction(final Action action) { 184 | setAction(action, true, ROTATE_CLOCKWISE); 185 | } 186 | 187 | /** 188 | * Sets the new action. If an action was set before a transition will be started. 189 | * 190 | * @param action 191 | * @param rotation Can be either {@link #ROTATE_CLOCKWISE} or {@link #ROTATE_COUNTER_CLOCKWISE}. 192 | */ 193 | public void setAction(final Action action, final int rotation) { 194 | setAction(action, true, rotation); 195 | } 196 | 197 | /** 198 | * Sets the new action. 199 | * 200 | * @param action 201 | * @param animate If a prior action was set and {@code true} a transition will be started, otherwise not. 202 | */ 203 | public void setAction(final Action action, final boolean animate) { 204 | setAction(action, animate, ROTATE_CLOCKWISE); 205 | } 206 | 207 | /** 208 | * Sets a new action transition. 209 | * 210 | * @param fromAction The initial action. 211 | * @param toAction The target action. 212 | * @param rotation The rotation direction used for the transition {@link #ROTATE_CLOCKWISE} or {@link #ROTATE_COUNTER_CLOCKWISE}. 213 | * @param delay The delay in ms before the transition is started. 214 | */ 215 | public void setAction(final Action fromAction, final Action toAction, final int rotation, long delay) { 216 | setAction(fromAction, false, ROTATE_CLOCKWISE); 217 | postDelayed(new Runnable() { 218 | @Override 219 | public void run() { 220 | if (!isAttachedToWindow()) { 221 | return; 222 | } 223 | setAction(toAction, true, rotation); 224 | } 225 | }, delay); 226 | } 227 | 228 | /** 229 | * Set the animation duration used for Action transitions 230 | * 231 | * @param animationDuration the duration in ms 232 | */ 233 | public void setAnimationDuration(long animationDuration) { 234 | this.animationDuration = animationDuration; 235 | } 236 | 237 | @Override 238 | protected Parcelable onSaveInstanceState() { 239 | final Parcelable superState = super.onSaveInstanceState(); 240 | final SavedState ss = new SavedState(superState); 241 | ss.currentAction = currentAction; 242 | ss.color = color; 243 | return ss; 244 | } 245 | 246 | @Override 247 | protected void onRestoreInstanceState(Parcelable state) { 248 | SavedState ss = (SavedState) state; 249 | super.onRestoreInstanceState(ss.getSuperState()); 250 | this.color = ss.color; 251 | this.currentAction = ss.currentAction; 252 | this.animationProgress = 1f; 253 | } 254 | 255 | private void setAction(Action action, boolean animate, int rotation) { 256 | if (action == null) { 257 | return; 258 | } 259 | 260 | this.rotation = rotation; 261 | if (currentAction == null) { 262 | currentAction = action; 263 | currentAction.flipHorizontally(); 264 | animationProgress = 1f; 265 | UiHelper.postInvalidateOnAnimation(this); 266 | return; 267 | } 268 | 269 | if (currentAction.getClass().equals(action.getClass())) { 270 | return; 271 | } 272 | 273 | oldAction = currentAction; 274 | currentAction = action; 275 | 276 | if (animate) { 277 | animationProgress = 0f; 278 | if (ready) { 279 | startAnimation(); 280 | } else { 281 | animateWhenReady = true; 282 | } 283 | } else { 284 | animationProgress = 1f; 285 | UiHelper.postInvalidateOnAnimation(this); 286 | } 287 | } 288 | 289 | private void updatePath(Action action) { 290 | path.reset(); 291 | 292 | final float[] data = action.getLineData(); 293 | // Once we're near the end of the animation we use the action segments to draw linked lines 294 | if (animationProgress > 0.95f && !action.getLineSegments().isEmpty()) { 295 | for (LineSegment s : action.getLineSegments()) { 296 | path.moveTo(data[s.getStartIdx() + 0], data[s.getStartIdx() + 1]); 297 | path.lineTo(data[s.getStartIdx() + 2], data[s.getStartIdx() + 3]); 298 | for (int i = 1; i < s.indexes.length; i++) { 299 | path.lineTo(data[s.indexes[i] + 0], data[s.indexes[i] + 1]); 300 | path.lineTo(data[s.indexes[i] + 2], data[s.indexes[i] + 3]); 301 | } 302 | } 303 | } else { 304 | for (int i = 0; i < data.length; i += 4) { 305 | path.moveTo(data[i + 0], data[i + 1]); 306 | path.lineTo(data[i + 2], data[i + 3]); 307 | } 308 | } 309 | } 310 | 311 | private void transformActions() { 312 | if (currentAction != null && !currentAction.isTransformed()) { 313 | currentAction.transform(padding, padding, scale, size); 314 | } 315 | 316 | if (oldAction != null && !oldAction.isTransformed()) { 317 | oldAction.transform(padding, padding, scale, size); 318 | } 319 | } 320 | 321 | private void startAnimation() { 322 | oldAction.flipHorizontally(); 323 | currentAction.flipHorizontally(); 324 | 325 | transformActions(); 326 | 327 | animatedAction.setLineSegments(currentAction.getLineSegments()); 328 | final ObjectAnimator animator = ObjectAnimator.ofFloat(ActionView.this, "animationProgress", 0f, 1f); 329 | animator.setInterpolator(BakedBezierInterpolator.getInstance()); 330 | animator.setDuration(animationDuration).start(); 331 | } 332 | 333 | private Action getActionFromEnum(int id) { 334 | switch (id) { 335 | case 0: 336 | return new DrawerAction(); 337 | case 1: 338 | return new BackAction(); 339 | case 2: 340 | return new CloseAction(); 341 | case 3: 342 | return new PlusAction(); 343 | } 344 | 345 | return null; 346 | } 347 | 348 | static class SavedState extends BaseSavedState { 349 | 350 | Action currentAction; 351 | int color; 352 | 353 | public SavedState(Parcelable superState) { 354 | super(superState); 355 | } 356 | 357 | public SavedState(Parcel source) { 358 | super(source); 359 | currentAction = source.readParcelable(getClass().getClassLoader()); 360 | color = source.readInt(); 361 | } 362 | 363 | @Override 364 | public void writeToParcel(Parcel dest, int flags) { 365 | super.writeToParcel(dest, flags); 366 | dest.writeParcelable(currentAction, 0); 367 | dest.writeInt(color); 368 | } 369 | 370 | public static final Creator37 | * }