├── .gitignore
├── .idea
├── codeStyleSettings.xml
├── compiler.xml
├── encodings.xml
├── gradle.xml
├── kotlinc.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── ctech
│ │ └── reaction
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── Anger.json
│ │ ├── Haha.json
│ │ ├── Like.json
│ │ ├── Love.json
│ │ ├── Sorry.json
│ │ ├── Wow.json
│ │ └── option2b.json
│ ├── java
│ │ └── com
│ │ │ └── ctech
│ │ │ └── reaction
│ │ │ ├── MainActivity.java
│ │ │ ├── model
│ │ │ ├── Feed.java
│ │ │ └── Reaction.java
│ │ │ ├── ui
│ │ │ ├── FeedAdapter.java
│ │ │ └── FeedViewHolder.java
│ │ │ ├── util
│ │ │ ├── Constants.java
│ │ │ └── DisplayUtil.java
│ │ │ └── widget
│ │ │ ├── ArcTranslate.java
│ │ │ ├── Emotion.java
│ │ │ ├── ReactionView.java
│ │ │ └── RoundedBoard.java
│ └── res
│ │ ├── anim
│ │ ├── pop_in.xml
│ │ └── pop_out.xml
│ │ ├── drawable-hdpi
│ │ ├── ic_avatar.jpg
│ │ ├── ic_badge_like.png
│ │ ├── ic_comment.png
│ │ ├── ic_like.png
│ │ ├── ic_more.png
│ │ ├── ic_post.png
│ │ └── ic_share.png
│ │ ├── drawable
│ │ ├── label_background.xml
│ │ └── popup_background.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── item_feed.xml
│ │ ├── view_label.xml
│ │ └── view_reaction.xml
│ │ ├── menu
│ │ └── menu_main.xml
│ │ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ │ ├── values-v21
│ │ └── styles.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── integers.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── ctech
│ └── reaction
│ └── ExampleUnitTest.java
├── assets
└── demo.gif
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── keyframe
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ ├── assets
│ │ └── sample_file
│ ├── java
│ │ └── com
│ │ │ └── facebook
│ │ │ └── keyframes
│ │ │ └── SanityTests.java
│ └── res
│ │ └── drawable
│ │ ├── test_0.png
│ │ ├── test_1.png
│ │ ├── test_2.png
│ │ ├── test_3.png
│ │ ├── test_4.png
│ │ ├── test_5.png
│ │ ├── test_6.png
│ │ ├── test_7.png
│ │ ├── test_8.png
│ │ └── test_9.png
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── facebook
│ │ └── keyframes
│ │ ├── KFPath.java
│ │ ├── KeyframesDirectionallyScalingDrawable.java
│ │ ├── KeyframesDrawable.java
│ │ ├── KeyframesDrawableAnimationCallback.java
│ │ ├── KeyframesDrawableBuilder.java
│ │ ├── deserializers
│ │ ├── AbstractListDeserializer.java
│ │ ├── CommonDeserializerHelper.java
│ │ ├── KFAnimationDeserializer.java
│ │ ├── KFAnimationFrameDeserializer.java
│ │ ├── KFAnimationGroupDeserializer.java
│ │ ├── KFColorFrameDeserializer.java
│ │ ├── KFFeatureDeserializer.java
│ │ ├── KFFeatureEffectDeserializer.java
│ │ ├── KFFeatureFrameDeserializer.java
│ │ ├── KFGradientColorDeserializer.java
│ │ ├── KFGradientDeserializer.java
│ │ └── KFImageDeserializer.java
│ │ ├── model
│ │ ├── HasKeyFrame.java
│ │ ├── KFAnimation.java
│ │ ├── KFAnimationFrame.java
│ │ ├── KFAnimationGroup.java
│ │ ├── KFColorFrame.java
│ │ ├── KFFeature.java
│ │ ├── KFFeatureEffect.java
│ │ ├── KFFeatureFrame.java
│ │ ├── KFGradient.java
│ │ ├── KFGradientColor.java
│ │ ├── KFImage.java
│ │ └── keyframedmodels
│ │ │ ├── KeyFrameAnimationHelper.java
│ │ │ ├── KeyFramedAnchorPoint.java
│ │ │ ├── KeyFramedGradient.java
│ │ │ ├── KeyFramedMatrixAnimation.java
│ │ │ ├── KeyFramedObject.java
│ │ │ ├── KeyFramedOpacity.java
│ │ │ ├── KeyFramedPath.java
│ │ │ └── KeyFramedStrokeWidth.java
│ │ └── util
│ │ ├── AnimationGroupSort.java
│ │ ├── AnimationHelper.java
│ │ ├── ArgCheckUtil.java
│ │ ├── KFPathInterpolator.java
│ │ ├── ListHelper.java
│ │ └── VectorCommand.java
│ └── test
│ └── java
│ └── com
│ └── facebook
│ └── keyframes
│ ├── KFPathTest.java
│ ├── model
│ └── ModelTests.java
│ └── util
│ └── VectorCommandTest.java
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/*
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
229 |
230 |
231 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 1.8
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # FBReaction
2 |
3 | ## Facebook-like "Like with Reaction" demo for Android
4 |
5 |
6 | 
7 |
8 | Just like Facebook's app:
9 | - Press on like button
10 | - Keep your finger down, swipe left or right
11 | - Release your finger
12 |
13 | ### License
14 |
15 | ```java
16 | Copyright 2017 Cuong Le
17 |
18 | Licensed under the Apache License, Version 2.0 (the "License");
19 | you may not use this file except in compliance with the License.
20 | You may obtain a copy of the License at
21 |
22 | http://www.apache.org/licenses/LICENSE-2.0
23 |
24 | Unless required by applicable law or agreed to in writing, software
25 | distributed under the License is distributed on an "AS IS" BASIS,
26 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27 | See the License for the specific language governing permissions and
28 | limitations under the License.`
29 | ```
30 | ## Support project
31 |
32 | If you like the work **Facebook reaction** is doing please consider a small donation:
33 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | android {
3 | compileSdkVersion 27
4 | buildToolsVersion "27.0.1"
5 | defaultConfig {
6 | applicationId "com.ctech.reaction"
7 | minSdkVersion 21
8 | targetSdkVersion 27
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | compile fileTree(dir: 'libs', include: ['*.jar'])
23 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
24 | exclude group: 'com.android.support', module: 'support-annotations'
25 | })
26 |
27 | compile 'com.android.support:appcompat-v7:27.0.2'
28 | compile 'com.android.support:design:27.0.2'
29 | compile 'io.reactivex:rxjava:1.3.3'
30 | compile 'io.reactivex:rxandroid:1.2.1'
31 |
32 |
33 | compile 'com.jakewharton:butterknife:8.8.1'
34 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
35 |
36 | compile project(':keyframe')
37 |
38 | testCompile 'junit:junit:4.12'
39 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
40 | }
41 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/admin/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/ctech/reaction/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.ctech.reaction", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/assets/Love.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "LOVE",
3 | "key": 2,
4 | "frame_rate": 24,
5 | "animation_frame_count": 41,
6 | "animation_groups": [
7 | {
8 | "group_id": 1,
9 | "group_name": "Master Animation",
10 | "animations": [
11 | {
12 | "property": "SCALE",
13 | "key_values": [
14 | {
15 | "start_frame": 0,
16 | "data": [
17 | 100,
18 | 100
19 | ]
20 | },
21 | {
22 | "start_frame": 4,
23 | "data": [
24 | 97.1,
25 | 97.1
26 | ]
27 | },
28 | {
29 | "start_frame": 14,
30 | "data": [
31 | 111.3,
32 | 111.3
33 | ]
34 | },
35 | {
36 | "start_frame": 24,
37 | "data": [
38 | 98.2,
39 | 98.2
40 | ]
41 | },
42 | {
43 | "start_frame": 28,
44 | "data": [
45 | 101.15,
46 | 101.15
47 | ]
48 | },
49 | {
50 | "start_frame": 32,
51 | "data": [
52 | 100,
53 | 100
54 | ]
55 | },
56 | {
57 | "start_frame": 40,
58 | "data": [
59 | 100,
60 | 100
61 | ]
62 | }
63 | ],
64 | "timing_curves": [
65 | [
66 | [
67 | 0.16666666500000002,
68 | 0
69 | ],
70 | [
71 | 0.833333335,
72 | 1
73 | ]
74 | ],
75 | [
76 | [
77 | 0.16666666500000002,
78 | 0
79 | ],
80 | [
81 | 0.5,
82 | 1
83 | ]
84 | ],
85 | [
86 | [
87 | 0.5,
88 | 0
89 | ],
90 | [
91 | 0.833333335,
92 | 1
93 | ]
94 | ],
95 | [
96 | [
97 | 0.08333333333500001,
98 | 0
99 | ],
100 | [
101 | 0.833333335,
102 | 1
103 | ]
104 | ],
105 | [
106 | [
107 | 0.08333333333500001,
108 | 0
109 | ],
110 | [
111 | 0.833333335,
112 | 1
113 | ]
114 | ],
115 | [
116 | [
117 | 0.07908275266566,
118 | 0
119 | ],
120 | [
121 | 0.858296121933008,
122 | 1
123 | ]
124 | ]
125 | ],
126 | "anchor": [
127 | 88.5,
128 | 92.0807037353516
129 | ]
130 | }
131 | ]
132 | }
133 | ],
134 | "features": [
135 | {
136 | "name": "BG",
137 | "fill_color": "#fff25268",
138 | "key_frames": [
139 | {
140 | "start_frame": 0,
141 | "data": [
142 | "M176.25,88.25",
143 | "C176.25,136.85,136.85,176.25,88.25,176.25",
144 | "C39.65,176.25,0.25,136.85,0.25,88.25",
145 | "C0.25,39.65,39.65,0.25,88.25,0.25",
146 | "C136.85,0.25,176.25,39.65,176.25,88.25"
147 | ]
148 | }
149 | ]
150 | },
151 | {
152 | "name": "<3",
153 | "animation_group": 1,
154 | "fill_color": "#ffffffff",
155 | "key_frames": [
156 | {
157 | "start_frame": 0,
158 | "data": [
159 | "M115.53,45.66",
160 | "Q91.5,45.66,88.5,65.02",
161 | "Q85.5,45.66,61.47,45.66",
162 | "C38.36,45.66,31.62,69.23,34.43,81.84",
163 | "Q41.87,115.16,88.5,138.5",
164 | "Q135.13,115.16,142.57,81.84",
165 | "C145.38,69.23,138.64,45.66,115.53,45.66"
166 | ]
167 | }
168 | ]
169 | }
170 | ],
171 | "canvas_size": [
172 | 177,
173 | 177
174 | ]
175 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.support.v7.widget.LinearLayoutManager;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.view.View;
8 | import android.widget.FrameLayout;
9 | import android.widget.LinearLayout;
10 | import butterknife.BindView;
11 | import butterknife.ButterKnife;
12 | import butterknife.OnClick;
13 | import com.ctech.reaction.ui.FeedAdapter;
14 | import com.ctech.reaction.util.Constants;
15 | import com.ctech.reaction.widget.ReactionView;
16 |
17 | public class MainActivity extends AppCompatActivity {
18 |
19 | private static final String TAG = "MainActivity";
20 |
21 | //@BindView(R.id.rvNewFeed)
22 | RecyclerView rvNewFeed;
23 |
24 | @BindView(R.id.btReaction)
25 | LinearLayout btReaction;
26 |
27 | @BindView(R.id.root)
28 | FrameLayout root;
29 |
30 |
31 | @Override
32 | protected void onCreate(Bundle savedInstanceState) {
33 | super.onCreate(savedInstanceState);
34 | setContentView(R.layout.activity_main);
35 | ButterKnife.bind(this);
36 |
37 | }
38 |
39 | private boolean scaledDown;
40 |
41 |
42 | @OnClick(R.id.btReaction)
43 | public void showPopup(View v){
44 | ReactionView rvl = new ReactionView(this);
45 | root.addView(rvl);
46 | }
47 |
48 |
49 |
50 | private void setUpRecyclerView() {
51 | FeedAdapter adapter = new FeedAdapter(Constants.feeds);
52 | rvNewFeed.setLayoutManager(new LinearLayoutManager(this));
53 | rvNewFeed.setAdapter(adapter);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/model/Feed.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.model;
2 |
3 | /**
4 | * Created by KenZira on 2/2/17.
5 | */
6 |
7 | public class Feed {
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/model/Reaction.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.model;
2 |
3 | /**
4 | * Created by KenZira on 2/1/17.
5 | */
6 |
7 | public class Reaction {
8 |
9 | public String type;
10 | public String source;
11 |
12 | public Reaction(String type, String source) {
13 | this.type = type;
14 | this.source = source;
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/ui/FeedAdapter.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.ui;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import com.ctech.reaction.R;
8 | import com.ctech.reaction.model.Feed;
9 | import java.util.List;
10 |
11 | /**
12 | * Created by KenZira on 2/2/17.
13 | */
14 |
15 | public class FeedAdapter extends RecyclerView.Adapter {
16 | private List feeds;
17 |
18 | public FeedAdapter(List feeds) {
19 | this.feeds = feeds;
20 | }
21 |
22 | @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
23 | View v = LayoutInflater.from(parent.getContext())
24 | .inflate(R.layout.item_feed, parent, false);
25 | return new FeedViewHolder(v);
26 | }
27 |
28 | @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
29 |
30 | }
31 |
32 | @Override public int getItemCount() {
33 | return feeds.size();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/ui/FeedViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.ui;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.Gravity;
5 | import android.view.View;
6 | import android.view.WindowManager;
7 | import android.widget.ImageView;
8 | import android.widget.PopupWindow;
9 | import butterknife.BindView;
10 | import butterknife.ButterKnife;
11 | import butterknife.OnClick;
12 | import com.ctech.reaction.R;
13 |
14 | /**
15 | * Created by KenZira on 2/2/17.
16 | */
17 |
18 | public class FeedViewHolder extends RecyclerView.ViewHolder {
19 | @BindView(R.id.btLike)
20 | View btLike;
21 |
22 | @BindView(R.id.btComment)
23 | View btComment;
24 |
25 | @BindView(R.id.btShare)
26 | View btShare;
27 |
28 | @BindView(R.id.llFeedInfo)
29 | View llFeedInfo;
30 |
31 | @BindView(R.id.ivBadgeLike)
32 | ImageView ivBadgeLike;
33 |
34 | private final PopupWindow popupWindow;
35 |
36 | public FeedViewHolder(View itemView) {
37 | super(itemView);
38 | ButterKnife.bind(this, itemView);
39 |
40 | // final PopupReactionView popupView = new PopupReactionView(itemView.getContext());
41 | popupWindow = new PopupWindow(
42 | null,
43 | WindowManager.LayoutParams.WRAP_CONTENT,
44 | WindowManager.LayoutParams.WRAP_CONTENT);
45 | popupWindow.setAnimationStyle(R.style.PopupAnimation);
46 | }
47 |
48 | @OnClick(R.id.btLike)
49 | public void handleLikeClick() {
50 | if (popupWindow.isShowing()) {
51 | popupWindow.dismiss();
52 | } else {
53 | popupWindow.showAtLocation(llFeedInfo, Gravity.TOP | Gravity.START, ivBadgeLike.getRight(),
54 | llFeedInfo.getBottom() );
55 | }
56 | }
57 |
58 | @OnClick(R.id.btComment)
59 | public void handleCommentClick() {
60 |
61 | }
62 |
63 | @OnClick(R.id.btShare)
64 | public void handleShareClick() {
65 |
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/util/Constants.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.util;
2 |
3 | import com.ctech.reaction.model.Feed;
4 | import java.util.Arrays;
5 | import java.util.List;
6 |
7 | /**
8 | * Created by KenZira on 2/1/17.
9 | */
10 |
11 | public class Constants {
12 |
13 | public static int HORIZONTAL_SPACING = DisplayUtil.dpToPx(4);
14 |
15 | public static int VERTICAL_SPACING = DisplayUtil.dpToPx(8);
16 |
17 | public static int HEIGHT_VIEW_REACTION = DisplayUtil.dpToPx(260);
18 |
19 | public static final int MAX_ALPHA = 255;
20 |
21 | public static final List feeds = Arrays.asList(new Feed(), new Feed());
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/util/DisplayUtil.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.util;
2 |
3 | import android.content.res.Resources;
4 | import android.util.DisplayMetrics;
5 |
6 | /**
7 | * Created by KenZira on 3/18/17.
8 | */
9 |
10 | public class DisplayUtil {
11 |
12 | public static int dpToPx(int dp) {
13 | DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
14 | return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/widget/ArcTranslate.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.widget;
2 |
3 | import android.graphics.Point;
4 | import android.graphics.PointF;
5 | import android.view.animation.Animation;
6 | import android.view.animation.Transformation;
7 |
8 | public class ArcTranslate extends Animation {
9 |
10 | private final float mFromXValue;
11 | private final float mToXValue;
12 | private final float mYValue;
13 | private final int mFromXType;
14 | private final int mToXType;
15 | private final int mYType;
16 | private PointF start;
17 | private PointF end;
18 | private PointF middle;
19 |
20 | public ArcTranslate(long duration, int fromXType, float fromXValue,
21 | int toXType, float toXValue, int yType, float yValue) {
22 | setDuration(duration);
23 |
24 | mFromXValue = fromXValue;
25 | mToXValue = toXValue;
26 | mYValue = yValue;
27 |
28 | mFromXType = fromXType;
29 | mToXType = toXType;
30 | mYType = yType;
31 | }
32 |
33 | /**
34 | * Calculate the position on a quadratic bezier curve given three points
35 | * and the percentage of time passed.
36 | * from http://en.wikipedia.org/wiki/B%C3%A9zier_curve
37 | *
38 | * @param interpolatedTime - the fraction of the duration that has passed where 0<=time<=1
39 | * @param p0 - a single dimension of the starting point
40 | * @param p1 - a single dimension of the middle point
41 | * @param p2 - a single dimension of the ending point
42 | */
43 | private long calcBezier(float interpolatedTime, float p0, float p1, float p2) {
44 | return Math.round((Math.pow((1 - interpolatedTime), 2) * p0)
45 | + (2 * (1 - interpolatedTime) * interpolatedTime * p1)
46 | + (Math.pow(interpolatedTime, 2) * p2));
47 | }
48 |
49 | @Override
50 | protected void applyTransformation(float interpolatedTime, Transformation t) {
51 | float dx = calcBezier(interpolatedTime, start.x, middle.x, end.x);
52 | float dy = calcBezier(interpolatedTime, start.y, middle.y, end.y);
53 | t.getMatrix().setTranslate(dx, dy);
54 | }
55 |
56 | @Override
57 | public void initialize(int width, int height, int parentWidth, int parentHeight) {
58 | super.initialize(width, height, parentWidth, parentHeight);
59 | float startX = resolveSize(mFromXType, mFromXValue, width, parentWidth);
60 | float endX = resolveSize(mToXType, mToXValue, width, parentWidth);
61 | float middleY = resolveSize(mYType, mYValue, width, parentWidth);
62 | float middleX = startX + ((endX - startX) / 2);
63 | start = new PointF( startX, 0);
64 | end = new PointF(endX, 0);
65 | middle = new PointF(middleX, middleY);
66 | }
67 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/widget/Emotion.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.widget;
2 |
3 | import android.content.Context;
4 | import android.content.res.AssetManager;
5 | import android.graphics.Bitmap;
6 | import android.graphics.Canvas;
7 | import android.graphics.Paint;
8 | import android.graphics.Rect;
9 | import android.graphics.RectF;
10 | import android.util.Log;
11 | import android.view.LayoutInflater;
12 | import android.widget.TextView;
13 | import com.ctech.reaction.R;
14 | import com.ctech.reaction.util.Constants;
15 | import com.ctech.reaction.util.DisplayUtil;
16 | import com.facebook.keyframes.KeyframesDirectionallyScalingDrawable;
17 | import com.facebook.keyframes.KeyframesDrawable;
18 | import com.facebook.keyframes.KeyframesDrawableBuilder;
19 | import com.facebook.keyframes.deserializers.KFImageDeserializer;
20 | import com.facebook.keyframes.model.KFImage;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 |
24 | /**
25 | * Created by KenZira on 3/10/17.
26 | */
27 |
28 | public class Emotion {
29 |
30 | static final int SMALL_SIZE = DisplayUtil.dpToPx(24);
31 | static final int MEDIUM_SIZE = DisplayUtil.dpToPx(32);
32 | static final int LARGE_SIZE = DisplayUtil.dpToPx(72);
33 |
34 | private static final int SPACING_TO_LABEL = DisplayUtil.dpToPx(16);
35 | private static final int MAX_WIDTH_TITLE = DisplayUtil.dpToPx(56);
36 |
37 | int size = 0;
38 |
39 | int startAnimatedSize;
40 | int endAnimatedSize;
41 |
42 | float x;
43 | float y;
44 |
45 | float startAnimatedX;
46 |
47 | float startAnimatedY;
48 | float endAnimatedY;
49 |
50 | private Paint textPaint;
51 |
52 | private KeyframesDrawable imageDrawable;
53 |
54 | private Rect imageBound;
55 | private RectF textBound;
56 |
57 | private Context context;
58 | private float labelRatio;
59 | private Bitmap imageTitle;
60 |
61 | Emotion(Context context, String title, String imageResource) {
62 | this.context = context;
63 |
64 | imageDrawable = new KeyframesDrawableBuilder().withImage(getKFImage(imageResource)).build();
65 | imageDrawable.startAnimation();
66 |
67 | textPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
68 |
69 | imageBound = new Rect();
70 | textBound = new RectF();
71 |
72 | snapShotLabelView(title);
73 | }
74 |
75 | private KFImage getKFImage(String fileName) {
76 | AssetManager assetManager = context.getAssets();
77 |
78 | InputStream stream;
79 | KFImage kfImage = null;
80 |
81 | try {
82 | stream = assetManager.open(fileName);
83 | kfImage = KFImageDeserializer.deserialize(stream);
84 | } catch (IOException e) {
85 | e.printStackTrace();
86 | }
87 |
88 | return kfImage;
89 | }
90 |
91 | private void snapShotLabelView(String title) {
92 | LayoutInflater inflater = LayoutInflater.from(context);
93 | TextView labelView = (TextView) inflater.inflate(R.layout.view_label, null, false);
94 | labelView.setText(title);
95 |
96 | int width = (int) context.getResources().getDimension(R.dimen.label_width);
97 | int height = (int) context.getResources().getDimension(R.dimen.label_height);
98 |
99 | labelRatio = width / height ;
100 | imageTitle = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
101 |
102 | Canvas canvas = new Canvas(imageTitle);
103 | labelView.layout(0, 0, width, height);
104 | labelView.draw(canvas);
105 | }
106 |
107 | private void setAlphaTitle(int alpha) {
108 | textPaint.setAlpha(alpha);
109 | }
110 |
111 | void draw(final Canvas canvas) {
112 | imageBound.set((int)x,(int) y, (int)x + size, (int)y + size);
113 | imageDrawable.setBounds(imageBound);
114 | imageDrawable.draw(canvas);
115 | drawLabel(canvas);
116 | }
117 |
118 | private void drawLabel(Canvas canvas) {
119 | int width = size - MEDIUM_SIZE ;
120 | int height = (int) (width / labelRatio);
121 |
122 | if (width <= 0) return;
123 |
124 | setAlphaTitle(Constants.MAX_ALPHA * width / MAX_WIDTH_TITLE);
125 |
126 | float x = this.x + (size - width) / 2;
127 | float y = this.y - SPACING_TO_LABEL - height;
128 |
129 | textBound.set(x, y, x + width, y + height);
130 | canvas.drawBitmap(imageTitle, null, textBound, textPaint);
131 | }
132 |
133 | void setCurrentSize(int currentSize) {
134 | if(currentSize > this.size){
135 | imageDrawable.setDirectionalScale(0.5F, 0.5F,
136 | KeyframesDirectionallyScalingDrawable.ScaleDirection.DOWN);
137 | }else {
138 | imageDrawable.setDirectionalScale(0.5F, 0.5F,
139 | KeyframesDirectionallyScalingDrawable.ScaleDirection.UP);
140 | }
141 | this.size = currentSize;
142 | }
143 |
144 | }
145 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/widget/ReactionView.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.widget;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.util.AttributeSet;
7 | import android.view.MotionEvent;
8 | import android.view.View;
9 | import android.view.animation.Animation;
10 | import android.view.animation.Transformation;
11 | import com.ctech.reaction.util.Constants;
12 | import java.util.Arrays;
13 | import java.util.List;
14 |
15 | /**
16 | * Created by KenZira on 3/10/17.
17 | */
18 |
19 | public class ReactionView extends View {
20 |
21 | private static final int SCALE_DURATION = 200;
22 | private static final int TRANSLATION_DURATION = 800;
23 | private static final int CHILD_TRANSLATION_DURATION = 300;
24 | private static final int CHILD_DELAY = 100;
25 | private static final int DRAW_DELAY = 50;
26 | private RoundedBoard board;
27 | private List emotions;
28 | private int selectedIndex = -1;
29 |
30 | private SelectingAnimation selectingAnimation;
31 | private DeselectAnimation deselectAnimation;
32 |
33 | private Runnable runnable = new Runnable() {
34 | @Override public void run() {
35 | invalidate();
36 | }
37 | };
38 |
39 | public ReactionView(Context context) {
40 | super(context);
41 | init();
42 | }
43 |
44 | public ReactionView(Context context, AttributeSet attrs) {
45 | super(context, attrs);
46 | init();
47 | }
48 |
49 | public ReactionView(Context context, AttributeSet attrs, int defStyleAttr) {
50 | super(context, attrs, defStyleAttr);
51 | init();
52 | }
53 |
54 | private void init() {
55 | board = new RoundedBoard();
56 | setLayerType(LAYER_TYPE_SOFTWARE, null);
57 |
58 | emotions = Arrays.asList(
59 | new Emotion(getContext(), "Like", "Like.json"),
60 | new Emotion(getContext(), "Love", "Love.json"),
61 | new Emotion(getContext(), "Haha", "Haha.json"),
62 | new Emotion(getContext(), "Wow", "Wow.json"),
63 | new Emotion(getContext(), "Sorry", "Sorry.json"),
64 | new Emotion(getContext(), "Angry", "Anger.json")
65 | );
66 |
67 | selectingAnimation = new SelectingAnimation();
68 | deselectAnimation = new DeselectAnimation();
69 | deselectAnimation.setAnimationListener(new Animation.AnimationListener() {
70 | @Override public void onAnimationStart(Animation animation) {}
71 |
72 | @Override public void onAnimationEnd(Animation animation) {
73 | selectedIndex = -1;
74 | }
75 | @Override public void onAnimationRepeat(Animation animation) {}
76 | });
77 |
78 | startAnimation(new TranslationAnimation());
79 | }
80 |
81 | @Override
82 | public boolean onTouchEvent(MotionEvent event) {
83 | switch (event.getAction()) {
84 | case MotionEvent.ACTION_DOWN:
85 | return true;
86 | case MotionEvent.ACTION_MOVE:
87 | for (int i = 0; i < emotions.size(); i++) {
88 | if (event.getX() > emotions.get(i).x &&
89 | event.getX() < emotions.get(i).x + emotions.get(i).size) {
90 | onSelect(i);
91 | break;
92 | }
93 | }
94 | return true;
95 | case MotionEvent.ACTION_UP:
96 | onDeselect();
97 | return true;
98 | }
99 | return super.onTouchEvent(event);
100 | }
101 |
102 | private void onSelect(int index) {
103 | if (selectedIndex == index) {
104 | return;
105 | }
106 |
107 | selectedIndex = index;
108 |
109 | selectingAnimation.prepare();
110 | startAnimation(selectingAnimation);
111 | }
112 |
113 | private void onDeselect() {
114 | deselectAnimation.prepare();
115 | startAnimation(deselectAnimation);
116 | }
117 |
118 | @Override
119 | protected void onDraw(final Canvas canvas) {
120 | board.draw(canvas);
121 | for (Emotion emotion : emotions) {
122 | emotion.draw(canvas);
123 | }
124 |
125 | postDelayed(runnable, DRAW_DELAY);
126 | }
127 |
128 | private void animateEmotions(float interpolatedTime) {
129 |
130 | for (Emotion emotion : emotions) {
131 | animateEmotionSize(emotion, interpolatedTime);
132 | animateEmotionPosition(emotion);
133 | }
134 | }
135 |
136 | private void animateEmotionPosition(Emotion emotion) {
137 | emotion.y = RoundedBoard.BASE_LINE - emotion.size;
138 |
139 | emotions.get(0).x = RoundedBoard.LEFT + Constants.HORIZONTAL_SPACING;
140 | emotions.get(emotions.size() - 1).x =
141 | RoundedBoard.LEFT + RoundedBoard.WIDTH - Constants.HORIZONTAL_SPACING
142 | - emotions.get(emotions.size() - 1).size;
143 |
144 | for (int i = 1; i < selectedIndex; i++) {
145 | emotions.get(i).x = emotions.get(i - 1).x + emotions.get(i - 1).size
146 | + Constants.HORIZONTAL_SPACING;
147 | }
148 |
149 | for (int i = emotions.size() - 2; i > selectedIndex; i--) {
150 | emotions.get(i).x = emotions.get(i + 1).x - emotions.get(i).size
151 | - Constants.HORIZONTAL_SPACING;
152 | }
153 |
154 | if (selectedIndex > 0) {
155 | emotions.get(selectedIndex).x = emotions.get(selectedIndex - 1).x
156 | + emotions.get(selectedIndex - 1).size + Constants.HORIZONTAL_SPACING;
157 | }
158 | }
159 |
160 | private void animateEmotionSize(Emotion emotion, float interpolatedTime) {
161 | emotion.setCurrentSize(emotion.startAnimatedSize +
162 | (int) (interpolatedTime * (emotion.endAnimatedSize - emotion.startAnimatedSize)));
163 | }
164 |
165 | private void animateRoundedBoard(float interpolatedTime) {
166 | board.setCurrentHeight(board.startAnimatedHeight + (interpolatedTime *
167 | (board.endAnimatedHeight - board.startAnimatedHeight)));
168 | }
169 |
170 | private class SelectingAnimation extends Animation {
171 |
172 | SelectingAnimation() {
173 | setDuration(SCALE_DURATION);
174 | }
175 |
176 | void prepare(){
177 | prepareEmotions();
178 | prepareRoundedBoard();
179 | }
180 |
181 | private void prepareEmotions() {
182 | for (int i = 0; i < emotions.size(); i++) {
183 | emotions.get(i).startAnimatedSize = emotions.get(i).size;
184 |
185 | if (i == selectedIndex) {
186 | emotions.get(i).endAnimatedSize = Emotion.LARGE_SIZE;
187 | } else {
188 | emotions.get(i).endAnimatedSize = Emotion.SMALL_SIZE;
189 | }
190 | }
191 | }
192 |
193 | private void prepareRoundedBoard() {
194 | board.startAnimatedHeight = board.height;
195 | board.endAnimatedHeight = RoundedBoard.SCALED_DOWN_HEIGHT;
196 | }
197 |
198 | @Override
199 | protected void applyTransformation(float interpolatedTime, Transformation t) {
200 | animateRoundedBoard(interpolatedTime);
201 | animateEmotions(interpolatedTime);
202 | }
203 | }
204 |
205 | private class DeselectAnimation extends Animation {
206 |
207 | DeselectAnimation() {
208 | setDuration(SCALE_DURATION);
209 | }
210 |
211 | void prepare(){
212 | prepareRoundedBoard();
213 | prepareEmotions();
214 | }
215 |
216 | private void prepareEmotions() {
217 | for (Emotion emotion : emotions) {
218 | emotion.startAnimatedSize = emotion.size;
219 | emotion.endAnimatedSize = Emotion.MEDIUM_SIZE;
220 | }
221 | }
222 |
223 | private void prepareRoundedBoard() {
224 | board.startAnimatedHeight = board.height;
225 | board.endAnimatedHeight = RoundedBoard.HEIGHT;
226 | }
227 |
228 | @Override
229 | protected void applyTransformation(float interpolatedTime, Transformation t) {
230 | animateRoundedBoard(interpolatedTime);
231 | animateEmotions(interpolatedTime);
232 | }
233 | }
234 |
235 | private class TranslationAnimation extends Animation {
236 |
237 | private static final int TRANSLATION_DISTANCE = 150;
238 | private final int EMOTION_RADIUS = Emotion.MEDIUM_SIZE / 2;
239 |
240 | TranslationAnimation() {
241 | setDuration(TRANSLATION_DURATION);
242 | prepareRoundedBoard();
243 | prepareEmotions();
244 | }
245 |
246 | private void prepareEmotions() {
247 | for (int i = 0; i < emotions.size(); i++) {
248 | emotions.get(i).endAnimatedY = RoundedBoard.TOP + Constants.VERTICAL_SPACING;
249 | emotions.get(i).startAnimatedY =
250 | emotions.get(i).y = RoundedBoard.BOTTOM + TRANSLATION_DISTANCE;
251 |
252 | emotions.get(i).startAnimatedX
253 | = emotions.get(i).x = i == 0 ? RoundedBoard.LEFT
254 | + Constants.HORIZONTAL_SPACING + (Emotion.MEDIUM_SIZE / 2)
255 | : emotions.get(i - 1).x + Emotion.MEDIUM_SIZE + Constants.HORIZONTAL_SPACING;
256 | }
257 | }
258 |
259 | private void prepareRoundedBoard() {
260 | board.startAnimatedY = board.y = RoundedBoard.BOTTOM + TRANSLATION_DISTANCE;
261 | board.endAnimatedY = RoundedBoard.TOP;
262 | }
263 |
264 | @Override
265 | protected void applyTransformation(float interpolatedTime, Transformation t) {
266 | translateEmotions(interpolatedTime);
267 | translateRoundedBoard();
268 | }
269 |
270 | private void translateEmotions(float interpolatedTime) {
271 | float currentTime = interpolatedTime * TRANSLATION_DURATION;
272 | for (int i = 0; i < emotions.size(); i++) {
273 |
274 | int delayOfChild = CHILD_DELAY * i;
275 |
276 | Emotion view = emotions.get(i);
277 | if ((currentTime > delayOfChild)) {
278 | if ((currentTime - delayOfChild) <= CHILD_TRANSLATION_DURATION) {
279 |
280 | float progressOfChild = ((currentTime - delayOfChild) / CHILD_TRANSLATION_DURATION);
281 | view.y = view.startAnimatedY +
282 | progressOfChild * (view.endAnimatedY - view.startAnimatedY);
283 |
284 | view.x = view.startAnimatedX - progressOfChild * EMOTION_RADIUS;
285 |
286 | view.setCurrentSize((int) (progressOfChild * Emotion.MEDIUM_SIZE));
287 | } else {
288 | view.x = view.startAnimatedX - EMOTION_RADIUS;
289 | view.y = view.endAnimatedY;
290 | view.setCurrentSize(Emotion.MEDIUM_SIZE);
291 | }
292 | }
293 | }
294 | }
295 |
296 | private void translateRoundedBoard() {
297 | Emotion firstEmoticon = emotions.get(0);
298 | float d =
299 | (firstEmoticon.y - firstEmoticon.startAnimatedY) / (firstEmoticon.endAnimatedY
300 | - firstEmoticon.startAnimatedY) * (board.endAnimatedY - board.startAnimatedY);
301 |
302 | board.y = board.startAnimatedY + d;
303 | }
304 |
305 | }
306 |
307 | }
308 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ctech/reaction/widget/RoundedBoard.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction.widget;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.graphics.RectF;
7 | import com.ctech.reaction.util.Constants;
8 | import com.ctech.reaction.util.DisplayUtil;
9 |
10 | /**
11 | * Created by KenZira on 3/10/17.
12 | */
13 |
14 | public class RoundedBoard {
15 |
16 | static final int WIDTH = 6 * Emotion.MEDIUM_SIZE + 7 * Constants.HORIZONTAL_SPACING;
17 |
18 | static final int HEIGHT = DisplayUtil.dpToPx(48);
19 |
20 | static final int SCALED_DOWN_HEIGHT = DisplayUtil.dpToPx(38);
21 |
22 | static final float LEFT = DisplayUtil.dpToPx(16);
23 |
24 | static final float BOTTOM = Constants.HEIGHT_VIEW_REACTION - 200;
25 |
26 | static final float TOP = BOTTOM - HEIGHT;
27 |
28 | static final float BASE_LINE = TOP + Emotion.MEDIUM_SIZE + Constants.VERTICAL_SPACING;
29 |
30 | float height = HEIGHT;
31 | float y;
32 |
33 | private float radius = height / 2;
34 |
35 | float startAnimatedHeight;
36 | float endAnimatedHeight;
37 |
38 | float startAnimatedY;
39 | float endAnimatedY;
40 |
41 | private Paint boardPaint;
42 | private RectF rect;
43 |
44 | RoundedBoard() {
45 | initPaint();
46 | rect = new RectF();
47 | }
48 |
49 | private void initPaint() {
50 | boardPaint = new Paint();
51 | boardPaint.setAntiAlias(true);
52 | boardPaint.setStyle(Paint.Style.FILL);
53 | boardPaint.setColor(Color.WHITE);
54 | boardPaint.setShadowLayer(5.0f, 0.0f, 2.0f, 0xFF000000);
55 | }
56 |
57 | void setCurrentHeight(float newHeight) {
58 | height = newHeight;
59 | y = BOTTOM - height;
60 | }
61 |
62 | void draw(Canvas canvas) {
63 | rect.set(LEFT, y, LEFT + WIDTH, y + height);
64 | canvas.drawRoundRect(rect, radius, radius, boardPaint);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/pop_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
11 |
12 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/anim/pop_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/drawable-hdpi/ic_avatar.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_badge_like.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/drawable-hdpi/ic_badge_like.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_comment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/drawable-hdpi/ic_comment.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_like.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/drawable-hdpi/ic_like.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/drawable-hdpi/ic_more.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_post.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/drawable-hdpi/ic_post.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_share.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/drawable-hdpi/ic_share.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/label_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/popup_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
19 |
20 |
24 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_feed.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
32 |
41 |
52 |
53 |
55 |
56 |
61 |
62 |
63 |
66 |
67 |
68 |
69 |
70 |
81 |
82 |
91 |
92 |
97 |
104 |
105 |
106 |
115 |
116 |
121 |
128 |
129 |
130 |
139 |
140 |
145 |
152 |
153 |
154 |
155 |
156 |
166 |
171 |
172 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_label.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/view_reaction.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
25 |
26 |
37 |
38 |
46 |
47 |
54 |
55 |
56 |
62 |
63 |
64 |
70 |
71 |
72 |
78 |
79 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/menu_main.xml:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 | #dcdee3
7 | #FFFFFF
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 | 56dp
7 | 24dp
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 150
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Reaction
3 | Settings
4 | Nguyễn Thùy Ý Nhi
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
17 |
18 |
20 |
22 |
23 |
29 |
30 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ctech/reaction/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ctech.reaction;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() throws Exception {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/assets/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/assets/demo.gif
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.0.1'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 |
16 | allprojects {
17 | repositories {
18 | jcenter()
19 | google()
20 | }
21 | }
22 |
23 | task clean(type: Delete) {
24 | delete rootProject.buildDir
25 | }
26 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Dec 17 20:59:21 ICT 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/keyframe/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/keyframe/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 27
5 | buildToolsVersion '27.0.1'
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 27
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | testOptions {
20 | unitTests.returnDefaultValues = true
21 | }
22 | }
23 |
24 | dependencies {
25 | testCompile 'junit:junit:4.12'
26 | testCompile 'org.mockito:mockito-core:1.10.19'
27 | }
28 |
29 |
30 | task createJar(type: Copy) {
31 | from('build/intermediates/bundles/release')
32 | into('libs/')
33 | include('classes.jar')
34 | // rename('classes.jar', 'keyframes-' + VERSION_NAME + '.jar')
35 | }
36 |
--------------------------------------------------------------------------------
/keyframe/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/markpeng/Desktop/stuff/android-sdk/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/keyframe/src/androidTest/java/com/facebook/keyframes/SanityTests.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes;
11 |
12 | import android.graphics.Bitmap;
13 | import android.graphics.BitmapFactory;
14 | import android.graphics.Canvas;
15 | import android.graphics.Color;
16 | import android.graphics.PorterDuff;
17 | import android.graphics.drawable.BitmapDrawable;
18 | import android.test.AndroidTestCase;
19 |
20 | import com.facebook.keyframes.model.KFImage;
21 | import com.facebook.keyframes.deserializers.KFImageDeserializer;
22 |
23 | import junit.framework.Assert;
24 |
25 | import java.io.InputStream;
26 |
27 | public class SanityTests extends AndroidTestCase {
28 |
29 | private static final int TEST_CANVAS_SIZE_PX = 500;
30 |
31 | /**
32 | * Sanity deserializer test to make sure a valid file deserializes into a proper object.
33 | */
34 | public void testDeserializeValidFile() throws Exception {
35 | InputStream stream = getContext().getResources().getAssets().open("sample_file");
36 | KFImage deserializedModel = KFImageDeserializer.deserialize(stream);
37 | Assert.assertNotNull(deserializedModel);
38 | }
39 |
40 | /**
41 | * Sanity rendering test that renders the sample image and checks frames at 10% progress
42 | * increments, comparing images with a pre-generated set.
43 | */
44 | public void testFramesForSample() throws Exception {
45 | InputStream stream = getContext().getResources().getAssets().open("sample_file");
46 | KFImage kfImage = KFImageDeserializer.deserialize(stream);
47 | Bitmap testBitmap =
48 | Bitmap.createBitmap(TEST_CANVAS_SIZE_PX, TEST_CANVAS_SIZE_PX, Bitmap.Config.ARGB_8888);
49 | Canvas canvas = new Canvas(testBitmap);
50 |
51 | KeyframesDrawable testDrawable = new KeyframesDrawableBuilder().withImage(kfImage).build();
52 | testDrawable.setBounds(0, 0, TEST_CANVAS_SIZE_PX, TEST_CANVAS_SIZE_PX);
53 |
54 | float step = .1f;
55 | for (float progress = 0; progress <= 1; progress += step) {
56 | int resIdentifier =
57 | getContext().getResources().getIdentifier(
58 | "test_" + (int) (progress / step),
59 | "drawable",
60 | getContext().getPackageName());
61 | Bitmap compareBitmap =
62 | BitmapFactory.decodeStream(getContext().getResources().openRawResource(resIdentifier));
63 |
64 | testDrawable.setFrameProgress(kfImage.getFrameCount() * progress);
65 | canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
66 | testDrawable.draw(canvas);
67 | Assert.assertTrue(testBitmap.sameAs(compareBitmap));
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_0.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_1.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_2.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_3.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_4.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_5.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_6.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_7.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_8.png
--------------------------------------------------------------------------------
/keyframe/src/androidTest/res/drawable/test_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dbof10/FBReaction/f69640ae879b82449b251c3c78b45ac3e0348776/keyframe/src/androidTest/res/drawable/test_9.png
--------------------------------------------------------------------------------
/keyframe/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/KFPath.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes;
11 |
12 | import android.graphics.Matrix;
13 | import android.graphics.Path;
14 |
15 | /**
16 | * A wrapper around the Android {@link android.graphics.Path} object, only revealing methods which
17 | * we will use in Keyframes, as well as some basic ability to keep track of the last point set.
18 | */
19 | public class KFPath {
20 |
21 | private final Path mPath;
22 | private final float[] mLastPoint;
23 |
24 | public KFPath() {
25 | this(new Path(), new float[]{0, 0});
26 | }
27 |
28 | /**
29 | * Constructor for tests to pass in objects
30 | */
31 | KFPath(Path path, float[] lastPoint) {
32 | mPath = path;
33 | mLastPoint = lastPoint;
34 | }
35 |
36 | /**
37 | * Returns the last point, which should *never* be modified outside of this class.
38 | */
39 | public float[] getLastPoint() {
40 | return mLastPoint;
41 | }
42 |
43 | /**
44 | * See {@link Path#reset()}.
45 | */
46 | public void reset() {
47 | mPath.reset();
48 | adjustLastPoint(0, 0);
49 | }
50 |
51 | /**
52 | * See {@link Path#moveTo(float, float)}.
53 | */
54 | public void moveTo(float x, float y) {
55 | mPath.moveTo(x, y);
56 | adjustLastPoint(x, y);
57 | }
58 |
59 | /**
60 | * See {@link Path#rMoveTo(float, float)}.
61 | */
62 | public void rMoveTo(float dx, float dy) {
63 | mPath.rMoveTo(dx, dy);
64 | rAdjustLastPoint(dx, dy);
65 | }
66 |
67 | /**
68 | * See {@link Path#lineTo(float, float)}.
69 | */
70 | public void lineTo(float x, float y) {
71 | mPath.lineTo(x, y);
72 | adjustLastPoint(x, y);
73 | }
74 |
75 | /**
76 | * See {@link Path#rLineTo(float, float)}.
77 | */
78 | public void rLineTo(float dx, float dy) {
79 | mPath.rLineTo(dx, dy);
80 | rAdjustLastPoint(dx, dy);
81 | }
82 |
83 | /**
84 | * See {@link Path#quadTo(float, float, float, float)}.
85 | */
86 | public void quadTo(float x1, float y1, float x2, float y2) {
87 | mPath.quadTo(x1, y1, x2, y2);
88 | adjustLastPoint(x2, y2);
89 | }
90 |
91 | /**
92 | * See {@link Path#rQuadTo(float, float, float, float)}.
93 | */
94 | public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
95 | mPath.rQuadTo(dx1, dy1, dx2, dy2);
96 | rAdjustLastPoint(dx2, dy2);
97 | }
98 |
99 | /**
100 | * See {@link Path#cubicTo(float, float, float, float, float, float)}.
101 | */
102 | public void cubicTo(
103 | float x1,
104 | float y1,
105 | float x2,
106 | float y2,
107 | float x3,
108 | float y3) {
109 | mPath.cubicTo(x1, y1, x2, y2, x3, y3);
110 | adjustLastPoint(x3, y3);
111 | }
112 |
113 | /**
114 | * See {@link Path#rCubicTo(float, float, float, float, float, float)}.
115 | */
116 | public void rCubicTo(
117 | float dx1,
118 | float dy1,
119 | float dx2,
120 | float dy2,
121 | float dx3,
122 | float dy3) {
123 | mPath.rCubicTo(dx1, dy1, dx2, dy2, dx3, dy3);
124 | rAdjustLastPoint(dx3, dy3);
125 | }
126 |
127 | /**
128 | * See {@link Path#transform(Matrix)}.
129 | */
130 | public void transform(Matrix matrix) {
131 | mPath.transform(matrix);
132 | matrix.mapPoints(mLastPoint);
133 | }
134 |
135 | /**
136 | * See {@link Path#isEmpty()}.
137 | */
138 | public boolean isEmpty() {
139 | return mPath.isEmpty();
140 | }
141 |
142 | /**
143 | * Returns this path, which should *never* be modified outside of this class.
144 | */
145 | protected Path getPath() {
146 | return mPath;
147 | }
148 |
149 | /**
150 | * Moves mLastPoint to (x, y).
151 | */
152 | private void adjustLastPoint(float x, float y) {
153 | mLastPoint[0] = x;
154 | mLastPoint[1] = y;
155 | }
156 |
157 | /**
158 | * Moves mLastPoint by (+dx, +dy).
159 | */
160 | private void rAdjustLastPoint(float dx, float dy) {
161 | mLastPoint[0] += dx;
162 | mLastPoint[1] += dy;
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/KeyframesDirectionallyScalingDrawable.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes;
11 |
12 | public interface KeyframesDirectionallyScalingDrawable {
13 |
14 | /**
15 | * The direction to scale an image from the end. If direction UP is used, the origin of the
16 | * scaling will be the bottom edge of the image. Likewise, if DOWN is used, the origin will be
17 | * the top edge of the image.
18 | */
19 | enum ScaleDirection {
20 | UP,
21 | DOWN
22 | }
23 |
24 | /**
25 | * Sets special directional scale for the drawable. This can cause the drawable to draw outside
26 | * of the initially set bounds, but retains image quality when scaling up/down and allows
27 | * fractional scaling not bound by integers.
28 | * @param scaleFromCenter Scale value from the center of the image
29 | * @param scaleFromEnd Scale value from the end of the image, either the bottom or top.
30 | * @param direction The {@link ScaleDirection} to scale towards, used for scaleFromEnd param
31 | */
32 | void setDirectionalScale(float scaleFromCenter, float scaleFromEnd, ScaleDirection direction);
33 | }
34 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/KeyframesDrawableAnimationCallback.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes;
11 |
12 | import android.annotation.TargetApi;
13 | import android.os.Build;
14 | import android.os.Handler;
15 | import android.os.Looper;
16 | import android.os.SystemClock;
17 | import android.view.Choreographer;
18 |
19 | import com.facebook.keyframes.model.KFImage;
20 |
21 | import java.lang.ref.WeakReference;
22 |
23 | /**
24 | * A simple callback that when run, will call back indefinitely with progress updates until
25 | * cancelled. This will continuously feed back progress data from [0, 1] calculated by millis
26 | * per loop.
27 | */
28 | public abstract class KeyframesDrawableAnimationCallback {
29 |
30 | /**
31 | * And interface for a class which wants to listen to progress updates.
32 | */
33 | public interface FrameListener {
34 | void onProgressUpdate(float frameProgress);
35 | void onStop();
36 | }
37 |
38 | private final WeakReference mListener;
39 | private final int mFrameCount;
40 | private final int mMillisPerLoop;
41 |
42 | private long mStartTimeMillis;
43 | private boolean mStopAtLoopEnd;
44 | private int mCurrentLoopNumber;
45 |
46 | private long mMinimumMillisBetweenProgressUpdates = -1;
47 | private long mPreviousProgressMillis = 0;
48 |
49 | /**
50 | * Creates a KeyframesDrawableAnimationCallback appropriate for the API level of the device.
51 | * @param listener The listener that will receive callbacks on updates to the value
52 | * @return A KeyframesDrawableAnimationCallback implementation
53 | */
54 | public static KeyframesDrawableAnimationCallback create(
55 | FrameListener listener,
56 | KFImage face) {
57 | if (hasChoreographer()) {
58 | return new FrameCallbackFaceAnimationCallback(
59 | listener,
60 | face.getFrameRate(),
61 | face.getFrameCount());
62 | } else {
63 | return new RunnableFaceAnimationCallback(
64 | listener,
65 | face.getFrameRate(),
66 | face.getFrameCount());
67 | }
68 | }
69 |
70 | private static boolean hasChoreographer() {
71 | return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
72 | }
73 |
74 | private KeyframesDrawableAnimationCallback(FrameListener listener, int frameRate, int frameCount) {
75 | mListener = new WeakReference<>(listener);
76 | mFrameCount = frameCount;
77 | mMillisPerLoop = Math.round(1000 * ((float) frameCount / frameRate));
78 | }
79 |
80 | /**
81 | * Set the maximum frame rate for this animation.
82 | * Consider using this for low end devices.
83 | * @param maxFrameRate
84 | */
85 | public void setMaxFrameRate(int maxFrameRate) {
86 | mMinimumMillisBetweenProgressUpdates = 1000 / maxFrameRate;
87 | }
88 |
89 | protected abstract void postCallback();
90 |
91 | protected abstract void cancelCallback();
92 |
93 | /**
94 | * Starts this animation callback and resets the start time.
95 | *
96 | * !IMPORTANT! This animator will run indefinitely, so it must be cancelled via #stop()
97 | * or #pause() when no longer in use!
98 | */
99 | public void start() {
100 | mStopAtLoopEnd = false;
101 | mStartTimeMillis = 0;
102 | mCurrentLoopNumber = -1;
103 | cancelCallback();
104 | postCallback();
105 | }
106 |
107 | /**
108 | * Starts the animation and plays it once
109 | */
110 | public void playOnce() {
111 | mStopAtLoopEnd = true;
112 | mStartTimeMillis = 0;
113 | mCurrentLoopNumber = 0;
114 | cancelCallback();
115 | postCallback();
116 | }
117 |
118 | /**
119 | * Stops the callbacks animation and resets the start time.
120 | */
121 | public void stop() {
122 | cancelCallback();
123 | mStartTimeMillis = 0;
124 | mCurrentLoopNumber = -1;
125 | mListener.get().onStop();
126 | }
127 |
128 | /**
129 | * Pauses the callbacks animation and saves start time.
130 | */
131 | public void pause() {
132 | cancelCallback();
133 | mStartTimeMillis *= -1;
134 | }
135 |
136 | /**
137 | * Resumes this animation callback.
138 | *
139 | * !IMPORTANT! This animator will run indefinitely, so it must be cancelled via #stop()
140 | * or #pause() when no longer in use!
141 | */
142 | public void resume() {
143 | mStopAtLoopEnd = false;
144 | cancelCallback();
145 | postCallback();
146 | }
147 |
148 |
149 | /**
150 | * Stops looping the animation, but finishes the current animation.
151 | */
152 | public void stopAtLoopEnd() {
153 | mStopAtLoopEnd = true;
154 | }
155 |
156 | protected void advanceAnimation(final long frameTimeMillis) {
157 | if (mListener.get() == null) {
158 | cancelCallback();
159 | mStartTimeMillis = 0;
160 | mPreviousProgressMillis = 0;
161 | mCurrentLoopNumber = -1;
162 | return;
163 | }
164 | if (mStartTimeMillis == 0) {
165 | mStartTimeMillis = frameTimeMillis;
166 | } else if (mStartTimeMillis < 0) {
167 | long pausedTimeMillis = frameTimeMillis - mPreviousProgressMillis;
168 | mStartTimeMillis = mStartTimeMillis * -1 + pausedTimeMillis;
169 | mPreviousProgressMillis += pausedTimeMillis;
170 | }
171 |
172 | int currentLoopNumber = (int) (frameTimeMillis - mStartTimeMillis) / mMillisPerLoop;
173 | final boolean loopHasEnded = currentLoopNumber > mCurrentLoopNumber;
174 | if (mStopAtLoopEnd && loopHasEnded) {
175 | mListener.get().onProgressUpdate(mFrameCount);
176 | stop();
177 | return;
178 | }
179 | long currentProgressMillis = (frameTimeMillis - mStartTimeMillis) % mMillisPerLoop;
180 |
181 | boolean shouldUpdateProgress = true;
182 | if (frameTimeMillis - mPreviousProgressMillis < mMinimumMillisBetweenProgressUpdates) {
183 | shouldUpdateProgress = false;
184 | } else {
185 | mPreviousProgressMillis = frameTimeMillis;
186 | }
187 |
188 | if (shouldUpdateProgress) {
189 | mListener.get().onProgressUpdate((float) currentProgressMillis / mMillisPerLoop * mFrameCount);
190 | }
191 | mCurrentLoopNumber = (int) (frameTimeMillis - mStartTimeMillis) / mMillisPerLoop;
192 | postCallback();
193 | }
194 |
195 | @TargetApi(16)
196 | private static class FrameCallbackFaceAnimationCallback extends KeyframesDrawableAnimationCallback
197 | implements Choreographer.FrameCallback {
198 |
199 | private final Choreographer mChoreographer;
200 |
201 | private FrameCallbackFaceAnimationCallback(
202 | FrameListener listener,
203 | int frameRate,
204 | int frameCount) {
205 | super(listener, frameRate, frameCount);
206 | mChoreographer = Choreographer.getInstance();
207 | }
208 |
209 | @Override
210 | public void doFrame(long frameTimeNanos) {
211 | advanceAnimation(frameTimeNanos / 1000000); // nanoseconds per millisecond
212 | }
213 |
214 | @Override
215 | protected void postCallback() {
216 | mChoreographer.postFrameCallback(this);
217 | }
218 |
219 | @Override
220 | protected void cancelCallback() {
221 | mChoreographer.removeFrameCallback(this);
222 | }
223 | }
224 |
225 | private static class RunnableFaceAnimationCallback extends KeyframesDrawableAnimationCallback
226 | implements Runnable {
227 | private static final int ANIMATION_MIN_STEP_TIME_MS = 25; // 40 fps
228 |
229 | private final Handler mHandler;
230 |
231 | private RunnableFaceAnimationCallback(
232 | FrameListener listener,
233 | int frameRate,
234 | int frameCount) {
235 | super(listener, frameRate, frameCount);
236 | mHandler = new Handler(Looper.getMainLooper());
237 | }
238 |
239 | @Override
240 | public void run() {
241 | advanceAnimation(SystemClock.uptimeMillis());
242 | }
243 |
244 | @Override
245 | protected void postCallback() {
246 | mHandler.postDelayed(this, ANIMATION_MIN_STEP_TIME_MS);
247 | }
248 |
249 | @Override
250 | protected void cancelCallback() {
251 | mHandler.removeCallbacks(this);
252 | }
253 | }
254 | }
255 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/KeyframesDrawableBuilder.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes;
11 |
12 | import android.graphics.Bitmap;
13 |
14 | import com.facebook.keyframes.model.KFImage;
15 |
16 | import java.util.Map;
17 |
18 | /**
19 | * A class for building an instance of the Keyframes drawable. This class includes setting configs
20 | * allowed on a KeyframesDrawable, as well as access to experimental features via
21 | * {@link ExperimentalFeatures}.
22 | */
23 | public class KeyframesDrawableBuilder {
24 |
25 | private KFImage mImage;
26 | private int mMaxFrameRate = 60;
27 | private ExperimentalFeatures mExperimentalFeatures = new ExperimentalFeatures();
28 |
29 | public KeyframesDrawable build() {
30 | if (mImage == null) {
31 | throw new IllegalArgumentException("No KFImage provided!");
32 | }
33 | return new KeyframesDrawable(this);
34 | }
35 |
36 | public KeyframesDrawableBuilder withImage(KFImage image) {
37 | mImage = image;
38 | return this;
39 | }
40 |
41 | KFImage getImage() {
42 | return mImage;
43 | }
44 |
45 | public KeyframesDrawableBuilder withMaxFrameRate(int maxFrameRate) {
46 | mMaxFrameRate = maxFrameRate;
47 | return this;
48 | }
49 |
50 | int getMaxFrameRate() {
51 | return mMaxFrameRate;
52 | }
53 |
54 | // Experimental features below. APIs are volatile and subject to change. Use with care!
55 |
56 | public ExperimentalFeatures withExperimentalFeatures() {
57 | return mExperimentalFeatures;
58 | }
59 |
60 | ExperimentalFeatures getExperimentalFeatures() {
61 | return mExperimentalFeatures;
62 | }
63 |
64 | /**
65 | * Experimental features that are subject to drastic changes. Use with care!
66 | */
67 | public class ExperimentalFeatures {
68 | private Map mBitmaps;
69 | private boolean mClipToAECanvas = false;
70 |
71 | public KeyframesDrawable build() {
72 | return KeyframesDrawableBuilder.this.build();
73 | }
74 |
75 | public ExperimentalFeatures withBitmaps() {
76 | mBitmaps = mImage.getBitmaps();
77 | return ExperimentalFeatures.this;
78 | }
79 |
80 | Map getBitmaps() {
81 | return mBitmaps;
82 | }
83 |
84 | public ExperimentalFeatures clipToAECanvas() {
85 | mClipToAECanvas = true;
86 | return ExperimentalFeatures.this;
87 | }
88 |
89 | boolean getClipToAECanvas() {
90 | return mClipToAECanvas;
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/AbstractListDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | import android.util.JsonReader;
17 |
18 | /**
19 | * An generic abstract class to aid with deserializing a list of objects.
20 | * @param
21 | */
22 | public abstract class AbstractListDeserializer {
23 |
24 | /**
25 | * Given a {@link JsonReader} object at the start of a list, read through the list and return
26 | * a List with the given objects of type {@link T}.
27 | * @param reader The current {@link JsonReader} at the start of a list of {@link T}
28 | * @return List of type {@link T} objects
29 | * @throws IOException
30 | */
31 | public final List readList(JsonReader reader) throws IOException {
32 | List list = new ArrayList<>();
33 | reader.beginArray();
34 | while (reader.hasNext()) {
35 | list.add(this.readObjectImpl(reader));
36 | }
37 | reader.endArray();
38 | return list;
39 | }
40 |
41 | /**
42 | * Read one object of type {@link T} from the JsonReader and return it.
43 | * @param reader The current {@link JsonReader}
44 | * @return An object of type {@link T}
45 | * @throws IOException
46 | */
47 | abstract T readObjectImpl(JsonReader reader) throws IOException;
48 | }
49 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/CommonDeserializerHelper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 | import java.util.ArrayList;
14 | import java.util.Arrays;
15 | import java.util.List;
16 |
17 | import android.util.JsonReader;
18 |
19 | /**
20 | * Some useful common deserializers that are used by multiple Keyframes model objects.
21 | */
22 | public class CommonDeserializerHelper {
23 |
24 | /**
25 | * {@link AbstractListDeserializer} implementation for a list of {@link String}s.
26 | */
27 | public static final AbstractListDeserializer STRING_LIST_DESERIALIZER =
28 | new AbstractListDeserializer() {
29 | @Override
30 | String readObjectImpl(JsonReader reader) throws IOException {
31 | return reader.nextString();
32 | }
33 | };
34 |
35 | /**
36 | * {@link AbstractListDeserializer} implementation for a list of {@link Float}s.
37 | */
38 | private static final AbstractListDeserializer FLOAT_LIST_DESERIALIZER =
39 | new AbstractListDeserializer() {
40 | @Override
41 | Float readObjectImpl(JsonReader reader) throws IOException {
42 | return (float) reader.nextDouble();
43 | }
44 | };
45 |
46 | /**
47 | * {@link AbstractListDeserializer} implementation for a List>.
48 | */
49 | private static final AbstractListDeserializer> FLOAT_LIST_2D_DESERIALIZER =
50 | new AbstractListDeserializer>() {
51 | @Override
52 | List readObjectImpl(JsonReader reader) throws IOException {
53 | return FLOAT_LIST_DESERIALIZER.readList(reader);
54 | }
55 | };
56 |
57 | /**
58 | * {@link AbstractListDeserializer} implementation for a List>>.
59 | */
60 | private static final AbstractListDeserializer>> FLOAT_LIST_3D_DESERIALIZER =
61 | new AbstractListDeserializer>>() {
62 | @Override
63 | List> readObjectImpl(JsonReader reader) throws IOException {
64 | return FLOAT_LIST_2D_DESERIALIZER.readList(reader);
65 | }
66 | };
67 |
68 | /**
69 | * Reads in a float array from {@link JsonReader} and returns a primitive float array
70 | * @param reader The current {@link JsonReader}
71 | * @return a float[], containing floats parsed from {@link JsonReader}
72 | * @throws IOException
73 | */
74 | public static float[] readFloatArray(JsonReader reader) throws IOException {
75 | return convertListToPrimitiveArray(FLOAT_LIST_DESERIALIZER.readList(reader));
76 | }
77 |
78 | /**
79 | * Reads in a 3D float array from {@link JsonReader}, and returns a 3D primitive float array
80 | * @param reader The currnet {@link JsonReader}
81 | * @return a float[][][], containing the floats parsed from {@link JsonReader}
82 | * @throws IOException
83 | */
84 | public static float[][][] read3DFloatArray(JsonReader reader) throws IOException {
85 | return convert3DListToPrimitiveArray(FLOAT_LIST_3D_DESERIALIZER.readList(reader));
86 | }
87 |
88 | /**
89 | * A helper method which converts a List to a primitive array float[].
90 | * @param list The List to convert
91 | * @return a float[], representing the {@param list} passed in
92 | */
93 | private static float[] convertListToPrimitiveArray(List list) {
94 | float[] result = new float[list.size()];
95 | for (int i = 0, len = list.size(); i < len; i++) {
96 | result[i] = list.get(i);
97 | }
98 | return result;
99 | }
100 |
101 | /**
102 | * A helper method which converts List>> to a primitive array float[][][].
103 | * @param list The List>> to convert
104 | * @return a float[][][], representing the {@param list} passed in
105 | */
106 | private static float[][][] convert3DListToPrimitiveArray(List>> list) {
107 | float[][][] primaryArray = new float[list.size()][][];
108 | for (int i = 0, primaryLen = list.size(); i < primaryLen; i++) {
109 | float[][] secondaryArray = new float[list.get(0).size()][];
110 | for (int j = 0, secondaryLen = secondaryArray.length; j < secondaryLen; j++) {
111 | secondaryArray[j] = convertListToPrimitiveArray(list.get(i).get(j));
112 | }
113 | primaryArray[i] = secondaryArray;
114 | }
115 | return primaryArray;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFAnimationDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import android.util.JsonReader;
13 | import com.facebook.keyframes.model.KFAnimation;
14 |
15 | import java.io.IOException;
16 | import java.util.Locale;
17 |
18 | /**
19 | * Deserializer for {@link KFAnimation}.
20 | *
21 | * Root deserializer starts at {@link KFImageDeserializer}.
22 | */
23 | public class KFAnimationDeserializer {
24 |
25 | static final AbstractListDeserializer LIST_DESERIALIZER =
26 | new AbstractListDeserializer() {
27 | @Override
28 | KFAnimation readObjectImpl(JsonReader reader) throws IOException {
29 | return readObject(reader);
30 | }
31 | };
32 |
33 | public static KFAnimation readObject(JsonReader reader) throws IOException {
34 | reader.beginObject();
35 | KFAnimation.Builder builder = new KFAnimation.Builder();
36 | while (reader.hasNext()) {
37 | String name = reader.nextName();
38 | switch (name) {
39 | case KFAnimation.PROPERTY_TYPE_JSON_FIELD:
40 | builder.propertyType = KFAnimation.PropertyType.valueOf(
41 | reader.nextString().toUpperCase(Locale.US));
42 | break;
43 | case KFAnimation.ANIMATION_FRAMES_JSON_FIELD:
44 | builder.animationFrames =
45 | KFAnimationFrameDeserializer.LIST_DESERIALIZER.readList(reader);
46 | break;
47 | case KFAnimation.TIMING_CURVES_JSON_FIELD:
48 | builder.timingCurves = CommonDeserializerHelper.read3DFloatArray(reader);
49 | break;
50 | case KFAnimation.ANCHOR_JSON_FIELD:
51 | builder.anchor = CommonDeserializerHelper.readFloatArray(reader);
52 | break;
53 | default:
54 | reader.skipValue();
55 | }
56 | }
57 | reader.endObject();
58 | return builder.build();
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFAnimationFrameDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 |
14 | import android.util.JsonReader;
15 |
16 | import com.facebook.keyframes.model.KFAnimationFrame;
17 |
18 | /**
19 | * Deserializer for {@link KFAnimationFrame}.
20 | *
21 | * Root deserializer starts at {@link KFImageDeserializer}.
22 | */
23 | public class KFAnimationFrameDeserializer {
24 |
25 | static final AbstractListDeserializer LIST_DESERIALIZER =
26 | new AbstractListDeserializer() {
27 | @Override
28 | KFAnimationFrame readObjectImpl(JsonReader reader) throws IOException {
29 | return readObject(reader);
30 | }
31 | };
32 |
33 | public static KFAnimationFrame readObject(JsonReader reader) throws IOException {
34 | reader.beginObject();
35 | KFAnimationFrame.Builder builder = new KFAnimationFrame.Builder();
36 | while (reader.hasNext()) {
37 | String name = reader.nextName();
38 | switch (name) {
39 | case KFAnimationFrame.START_FRAME_JSON_FIELD:
40 | builder.startFrame = reader.nextInt();
41 | break;
42 | case KFAnimationFrame.DATA_JSON_FIELD:
43 | builder.data = CommonDeserializerHelper.readFloatArray(reader);
44 | break;
45 | default:
46 | reader.skipValue();
47 | }
48 | }
49 | reader.endObject();
50 | return builder.build();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFAnimationGroupDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 |
14 | import android.util.JsonReader;
15 |
16 | import com.facebook.keyframes.model.KFAnimationGroup;
17 |
18 | /**
19 | * Deserializer for {@link KFAnimationGroup}.
20 | *
21 | * Root deserializer starts at {@link KFImageDeserializer}.
22 | */
23 | public class KFAnimationGroupDeserializer {
24 |
25 | static final AbstractListDeserializer LIST_DESERIALIZER =
26 | new AbstractListDeserializer() {
27 | @Override
28 | KFAnimationGroup readObjectImpl(JsonReader reader) throws IOException {
29 | return readObject(reader);
30 | }
31 | };
32 |
33 | public static KFAnimationGroup readObject(JsonReader reader) throws IOException {
34 | reader.beginObject();
35 | KFAnimationGroup.Builder builder = new KFAnimationGroup.Builder();
36 | while (reader.hasNext()) {
37 | String name = reader.nextName();
38 | switch (name) {
39 | case KFAnimationGroup.GROUP_ID_JSON_FIELD:
40 | builder.groupId = reader.nextInt();
41 | break;
42 | case KFAnimationGroup.PARENT_GROUP_JSON_FIELD:
43 | builder.parentGroup = reader.nextInt();
44 | break;
45 | case KFAnimationGroup.ANIMATIONS_JSON_FIELD:
46 | builder.animations = KFAnimationDeserializer.LIST_DESERIALIZER.readList(reader);
47 | break;
48 | default:
49 | reader.skipValue();
50 | }
51 | }
52 | reader.endObject();
53 | return builder.build();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFColorFrameDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 |
14 | import android.graphics.Color;
15 | import android.util.JsonReader;
16 |
17 | import com.facebook.keyframes.model.KFColorFrame;
18 |
19 | /**
20 | * Deserializer for {@link KFColorFrame}.
21 | *
22 | * Root deserializer starts at {@link KFImageDeserializer}.
23 | */
24 | public class KFColorFrameDeserializer {
25 |
26 | static final AbstractListDeserializer LIST_DESERIALIZER =
27 | new AbstractListDeserializer() {
28 | @Override
29 | KFColorFrame readObjectImpl(JsonReader reader) throws IOException {
30 | return readObject(reader);
31 | }
32 | };
33 |
34 | public static KFColorFrame readObject(JsonReader reader) throws IOException {
35 | reader.beginObject();
36 | KFColorFrame.Builder builder = new KFColorFrame.Builder();
37 | while (reader.hasNext()) {
38 | String name = reader.nextName();
39 | switch (name) {
40 | case KFColorFrame.START_FRAME_JSON_FIELD:
41 | builder.startFrame = reader.nextInt();
42 | break;
43 | case KFColorFrame.COLOR_JSON_FIELD:
44 | builder.color = Color.parseColor(reader.nextString());
45 | break;
46 | default:
47 | reader.skipValue();
48 | }
49 | }
50 | reader.endObject();
51 | return builder.build();
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFFeatureDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import android.graphics.Color;
13 | import android.graphics.Paint;
14 | import android.util.JsonReader;
15 |
16 | import java.io.IOException;
17 | import java.util.Locale;
18 |
19 | import com.facebook.keyframes.model.KFFeature;
20 |
21 | /**
22 | * Deserializer for {@link KFFeature}.
23 | *
24 | * Root deserializer starts at {@link KFImageDeserializer}.
25 | */
26 | public class KFFeatureDeserializer {
27 |
28 | static final AbstractListDeserializer LIST_DESERIALIZER =
29 | new AbstractListDeserializer() {
30 | @Override
31 | KFFeature readObjectImpl(JsonReader reader) throws IOException {
32 | return readObject(reader);
33 | }
34 | };
35 |
36 | public static KFFeature readObject(JsonReader reader) throws IOException {
37 | reader.beginObject();
38 | KFFeature.Builder builder = new KFFeature.Builder();
39 | while (reader.hasNext()) {
40 | String name = reader.nextName();
41 | switch (name) {
42 | case KFFeature.NAME_JSON_FIELD:
43 | builder.name = reader.nextString();
44 | break;
45 | case KFFeature.FILL_COLOR_JSON_FIELD:
46 | builder.fillColor = Color.parseColor(reader.nextString());
47 | break;
48 | case KFFeature.STROKE_COLOR_JSON_FIELD:
49 | builder.strokeColor = Color.parseColor(reader.nextString());
50 | break;
51 | case KFFeature.STROKE_WIDTH_JSON_FIELD:
52 | builder.strokeWidth = (float) reader.nextDouble();
53 | break;
54 | case KFFeature.FROM_FRAME_JSON_FIELD:
55 | builder.fromFrame = (float) reader.nextDouble();
56 | break;
57 | case KFFeature.TO_FRAME_JSON_FIELD:
58 | builder.toFrame = (float) reader.nextDouble();
59 | break;
60 | case KFFeature.KEY_FRAMES_JSON_FIELD:
61 | builder.keyFrames = KFFeatureFrameDeserializer.LIST_DESERIALIZER.readList(reader);
62 | break;
63 | case KFFeature.TIMING_CURVES_JSON_FIELD:
64 | builder.timingCurves = CommonDeserializerHelper.read3DFloatArray(reader);
65 | break;
66 | case KFFeature.ANIMATION_GROUP_JSON_FIELD:
67 | builder.animationGroup = reader.nextInt();
68 | break;
69 | case KFFeature.FEATURE_ANIMATIONS_JSON_FIELD:
70 | builder.featureAnimations =
71 | KFAnimationDeserializer.LIST_DESERIALIZER.readList(reader);
72 | break;
73 | case KFFeature.EFFECT_JSON_FIELD:
74 | builder.effect = KFFeatureEffectDeserializer.readObject(reader);
75 | break;
76 | case KFFeature.STROKE_LINE_CAP_JSON_FIELD:
77 | builder.strokeLineCap = Paint.Cap.valueOf(reader.nextString().toUpperCase(Locale.US));
78 | break;
79 | case KFFeature.BACKED_IMAGE_NAME_JSON_FIELD:
80 | builder.backedImageName = reader.nextString();
81 | break;
82 | case KFFeature.FEATURE_MASK_JSON_FIELD:
83 | builder.featureMask = KFFeatureDeserializer.readObject(reader);
84 | break;
85 | default:
86 | reader.skipValue();
87 | }
88 | }
89 | reader.endObject();
90 | return builder.build();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFFeatureEffectDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 |
14 | import android.util.JsonReader;
15 |
16 | import com.facebook.keyframes.model.KFFeatureEffect;
17 |
18 | /**
19 | * Deserializer for {@link KFFeatureEffect}.
20 | *
21 | * Root deserializer starts at {@link KFImageDeserializer}.
22 | */
23 | public class KFFeatureEffectDeserializer {
24 |
25 | public static KFFeatureEffect readObject(JsonReader reader) throws IOException {
26 | reader.beginObject();
27 | KFFeatureEffect.Builder builder = new KFFeatureEffect.Builder();
28 | while (reader.hasNext()) {
29 | String name = reader.nextName();
30 | switch (name) {
31 | case KFFeatureEffect.GRADIENT_JSON_FIELD:
32 | builder.gradient = KFGradientDeserializer.readObject(reader);
33 | break;
34 | default:
35 | reader.skipValue();
36 | }
37 | }
38 | reader.endObject();
39 | return builder.build();
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFFeatureFrameDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 |
14 | import android.util.JsonReader;
15 |
16 | import com.facebook.keyframes.model.KFFeatureFrame;
17 |
18 | /**
19 | * Deserializer for {@link KFFeatureFrame}.
20 | *
21 | * Root deserializer starts at {@link KFImageDeserializer}.
22 | */
23 | public class KFFeatureFrameDeserializer {
24 |
25 | static final AbstractListDeserializer LIST_DESERIALIZER =
26 | new AbstractListDeserializer() {
27 | @Override
28 | KFFeatureFrame readObjectImpl(JsonReader reader) throws IOException {
29 | return readObject(reader);
30 | }
31 | };
32 |
33 | public static KFFeatureFrame readObject(JsonReader reader) throws IOException {
34 | reader.beginObject();
35 | KFFeatureFrame.Builder builder = new KFFeatureFrame.Builder();
36 | while (reader.hasNext()) {
37 | String name = reader.nextName();
38 | switch (name) {
39 | case KFFeatureFrame.START_FRAME_JSON_FIELD:
40 | builder.startFrame = reader.nextInt();
41 | break;
42 | case KFFeatureFrame.DATA_JSON_FIELD:
43 | builder.data = CommonDeserializerHelper.STRING_LIST_DESERIALIZER.readList(reader);
44 | break;
45 | default:
46 | reader.skipValue();
47 | }
48 | }
49 | reader.endObject();
50 | return builder.build();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFGradientColorDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 |
14 | import android.util.JsonReader;
15 |
16 | import com.facebook.keyframes.model.KFGradientColor;
17 |
18 | /**
19 | * Deserializer for {@link KFGradientColor}.
20 | *
21 | * Root deserializer starts at {@link KFImageDeserializer}.
22 | */
23 | public class KFGradientColorDeserializer {
24 |
25 | public static KFGradientColor readObject(JsonReader reader) throws IOException {
26 | reader.beginObject();
27 | KFGradientColor.Builder builder = new KFGradientColor.Builder();
28 | while (reader.hasNext()) {
29 | String name = reader.nextName();
30 | switch (name) {
31 | case KFGradientColor.KEY_VALUES_JSON_FIELD:
32 | builder.keyValues = KFColorFrameDeserializer.LIST_DESERIALIZER.readList(reader);
33 | break;
34 | case KFGradientColor.TIMING_CURVES_JSON_FIELD:
35 | builder.timingCurves = CommonDeserializerHelper.read3DFloatArray(reader);
36 | break;
37 | default:
38 | reader.skipValue();
39 | }
40 | }
41 | reader.endObject();
42 | return builder.build();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFGradientDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 |
14 | import android.util.JsonReader;
15 |
16 | import com.facebook.keyframes.model.KFGradient;
17 |
18 | /**
19 | * Deserializer for {@link KFGradient}.
20 | *
21 | * Root deserializer starts at {@link KFImageDeserializer}.
22 | */
23 | public class KFGradientDeserializer {
24 |
25 | static final AbstractListDeserializer LIST_DESERIALIZER =
26 | new AbstractListDeserializer() {
27 | @Override
28 | KFGradient readObjectImpl(JsonReader reader) throws IOException {
29 | return readObject(reader);
30 | }
31 | };
32 |
33 | public static KFGradient readObject(JsonReader reader) throws IOException {
34 | reader.beginObject();
35 | KFGradient.Builder builder = new KFGradient.Builder();
36 | while (reader.hasNext()) {
37 | String name = reader.nextName();
38 | switch (name) {
39 | case KFGradient.COLOR_START_JSON_FIELD:
40 | builder.colorStart = KFGradientColorDeserializer.readObject(reader);
41 | break;
42 | case KFGradient.COLOR_END_JSON_FIELD:
43 | builder.colorEnd = KFGradientColorDeserializer.readObject(reader);
44 | break;
45 | default:
46 | reader.skipValue();
47 | }
48 | }
49 | reader.endObject();
50 | return builder.build();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/deserializers/KFImageDeserializer.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.deserializers;
11 |
12 | import java.io.IOException;
13 | import java.io.InputStream;
14 | import java.io.InputStreamReader;
15 | import java.util.HashMap;
16 | import java.util.Map;
17 |
18 | import android.graphics.Bitmap;
19 | import android.graphics.BitmapFactory;
20 | import android.util.Base64;
21 | import android.util.JsonReader;
22 |
23 | import com.facebook.keyframes.model.KFImage;
24 |
25 | /**
26 | * Deserializer for {@link KFImage}. This is the root image object deserializer and contains the
27 | * {@link #deserialize(InputStream)} method, which takes an {@link InputStream} and attempts to
28 | * deserialize a full {@link KFImage} object from it.
29 | */
30 | public class KFImageDeserializer {
31 |
32 | /**
33 | * Given an input stream, attempt to deserialize a {@link KFImage} and return it.
34 | * @param inputStream An input stream containing JSON for a {@link KFImage}
35 | * @return A deserialized {@link KFImage} object
36 | * @throws IOException
37 | */
38 | public static KFImage deserialize(InputStream inputStream) throws IOException {
39 | JsonReader reader = new JsonReader(new InputStreamReader(inputStream));
40 | return readObject(reader);
41 | }
42 |
43 | static KFImage readObject(JsonReader reader) throws IOException {
44 | reader.beginObject();
45 | KFImage.Builder builder = new KFImage.Builder();
46 | while (reader.hasNext()) {
47 | String name = reader.nextName();
48 | switch (name) {
49 | case KFImage.FRAME_RATE_JSON_FIELD:
50 | builder.frameRate = reader.nextInt();
51 | break;
52 | case KFImage.FRAME_COUNT_JSON_FIELD:
53 | builder.frameCount = reader.nextInt();
54 | break;
55 | case KFImage.FEATURES_JSON_FIELD:
56 | builder.features =
57 | KFFeatureDeserializer.LIST_DESERIALIZER.readList(reader);
58 | break;
59 | case KFImage.ANIMATION_GROUPS_JSON_FIELD:
60 | builder.animationGroups =
61 | KFAnimationGroupDeserializer.LIST_DESERIALIZER.readList(reader);
62 | break;
63 | case KFImage.CANVAS_SIZE_JSON_FIELD:
64 | builder.canvasSize = CommonDeserializerHelper.readFloatArray(reader);
65 | break;
66 | case KFImage.KEY_JSON_FIELD:
67 | builder.key = reader.nextInt();
68 | break;
69 | case KFImage.BITMAPS_JSON_FIELD:
70 | builder.bitmaps = readBitmaps(reader);
71 | break;
72 | default:
73 | reader.skipValue();
74 | }
75 | }
76 | reader.endObject();
77 | return builder.build();
78 | }
79 |
80 | private static Map readBitmaps(JsonReader reader) throws IOException {
81 | reader.beginObject();
82 | Map bitmaps = new HashMap<>();
83 | while (reader.hasNext()) {
84 | String name = reader.nextName();
85 | byte[] bytes = Base64.decode(reader.nextString(), Base64.DEFAULT);
86 | Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
87 | bitmaps.put(name, bitmap);
88 | }
89 | reader.endObject();
90 | return bitmaps;
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/HasKeyFrame.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | /**
13 | * An interface to identify an object which has a key frame associated with it.
14 | */
15 | public interface HasKeyFrame {
16 | int getKeyFrame();
17 | }
18 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFAnimation.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | import java.util.Comparator;
13 | import java.util.List;
14 |
15 | import com.facebook.keyframes.model.keyframedmodels.KeyFramedAnchorPoint;
16 | import com.facebook.keyframes.model.keyframedmodels.KeyFramedMatrixAnimation;
17 | import com.facebook.keyframes.model.keyframedmodels.KeyFramedObject;
18 | import com.facebook.keyframes.model.keyframedmodels.KeyFramedOpacity;
19 | import com.facebook.keyframes.model.keyframedmodels.KeyFramedStrokeWidth;
20 | import com.facebook.keyframes.util.ArgCheckUtil;
21 | import com.facebook.keyframes.util.ListHelper;
22 |
23 | /**
24 | * The base class for describing an animated property. This can range from matrix based properties
25 | * such as scale, rotation, position, to others like stroke width.
26 | */
27 | public class KFAnimation {
28 |
29 | public static final Comparator ANIMATION_PROPERTY_COMPARATOR =
30 | new Comparator() {
31 | @Override
32 | public int compare(
33 | KFAnimation lhs, KFAnimation rhs) {
34 | return lhs.getPropertyType().compareTo(rhs.getPropertyType());
35 | }
36 | };
37 |
38 | /**
39 | * Enum for the different supported animation types, listed in order of transform application.
40 | * Standard transform ordering is SCALE * ROTATION * TRANSLATION.
41 | */
42 | public enum PropertyType {
43 | SCALE (true),
44 | ROTATION (true),
45 | POSITION (true),
46 | X_POSITION (true),
47 | Y_POSITION (true),
48 | ANCHOR_POINT (false),
49 | STROKE_WIDTH (false),
50 | OPACITY (false);
51 |
52 | /**
53 | * Whether this animation is matrix based or not. Currently, the only non-matrix based
54 | * animation is stroke width, which is specific to {@link KFFeature}.
55 | */
56 | final boolean mIsMatrixBased;
57 |
58 | PropertyType(boolean isMatrixBased) {
59 | mIsMatrixBased = isMatrixBased;
60 | }
61 |
62 | /**
63 | * Returns whether this animation is matrix based or not. Currently, the only non-matrix based
64 | * animation is stroke width, which is specific to {@link KFFeature}.
65 | */
66 | public boolean isMatrixBased() {
67 | return mIsMatrixBased;
68 | }
69 | }
70 |
71 | /**
72 | * The property animated by this KFAnimation instance.
73 | */
74 | public static final String PROPERTY_TYPE_JSON_FIELD = "property";
75 | private final PropertyType mPropertyType;
76 |
77 | /**
78 | * A list of {@link KFAnimationFrame}s, each describing timing and value data for a
79 | * specific key frame.
80 | */
81 | public static final String ANIMATION_FRAMES_JSON_FIELD = "key_values";
82 | private final List mAnimationFrames;
83 |
84 | /**
85 | * An array of timing curve data. This array describes transition values between each key frame
86 | * in mAnimationFrames. For each timing curve, there are two sets of points which are the control
87 | * points describing how to draw the cubic curve from (0,0) to (1,1).
88 | */
89 | public static final String TIMING_CURVES_JSON_FIELD = "timing_curves";
90 | private final float[][][] mTimingCurves;
91 |
92 | /**
93 | * An anchor point, which changes the origin of a matrix based property.
94 | * Deprecated in favor of the ANCHOR_POINT animation.
95 | */
96 | @Deprecated
97 | public static final String ANCHOR_JSON_FIELD = "anchor";
98 | @Deprecated
99 | private final float[] mAnchor;
100 |
101 | /**
102 | * A post-processed data structure containing cached information for this key frame animation.
103 | */
104 | private final KeyFramedObject mKeyFramedAnimation;
105 |
106 | public static class Builder {
107 | public PropertyType propertyType;
108 | public List animationFrames;
109 | public float[][][] timingCurves;
110 | public float[] anchor;
111 |
112 | public KFAnimation build() {
113 | return new KFAnimation(propertyType, animationFrames, timingCurves, anchor);
114 | }
115 | }
116 |
117 | public KFAnimation(
118 | PropertyType propertyType,
119 | List animationFrames,
120 | float[][][] timingCurves,
121 | float[] anchor) {
122 | mPropertyType = ArgCheckUtil.checkArg(
123 | propertyType,
124 | propertyType != null,
125 | PROPERTY_TYPE_JSON_FIELD);
126 | mAnimationFrames = ArgCheckUtil.checkArg(
127 | ListHelper.immutableOrEmpty(animationFrames),
128 | animationFrames != null && animationFrames.size() > 0,
129 | ANIMATION_FRAMES_JSON_FIELD);
130 | mTimingCurves = ArgCheckUtil.checkArg(
131 | timingCurves,
132 | ArgCheckUtil.checkTimingCurveObjectValidity(timingCurves, mAnimationFrames.size()),
133 | TIMING_CURVES_JSON_FIELD);
134 | mAnchor = ArgCheckUtil.checkArg(
135 | anchor,
136 | anchor == null || anchor.length == 2,
137 | ANCHOR_JSON_FIELD);
138 | if (mPropertyType.isMatrixBased()) {
139 | mKeyFramedAnimation = KeyFramedMatrixAnimation.fromAnimation(this);
140 | } else if (mPropertyType == PropertyType.STROKE_WIDTH){
141 | mKeyFramedAnimation = KeyFramedStrokeWidth.fromAnimation(this);
142 | } else if (mPropertyType == PropertyType.ANCHOR_POINT) {
143 | mKeyFramedAnimation = KeyFramedAnchorPoint.fromAnchorPoint(this);
144 | } else if (mPropertyType == PropertyType.OPACITY) {
145 | mKeyFramedAnimation = KeyFramedOpacity.fromAnimation(this);
146 | } else {
147 | throw new IllegalArgumentException(
148 | "Unknown property type for animation post processing: " + mPropertyType);
149 | }
150 | }
151 |
152 | public PropertyType getPropertyType() {
153 | return mPropertyType;
154 | }
155 |
156 | public List getAnimationFrames() {
157 | return mAnimationFrames;
158 | }
159 |
160 | public float[][][] getTimingCurves() {
161 | return mTimingCurves;
162 | }
163 |
164 | /**
165 | * Deprecated in favor of the ANCHOR_POINT animation.
166 | */
167 | @Deprecated
168 | public float[] getAnchor() {
169 | return mAnchor;
170 | }
171 |
172 | public KeyFramedObject getAnimation() {
173 | return mKeyFramedAnimation;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFAnimationFrame.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | import com.facebook.keyframes.util.ArgCheckUtil;
13 |
14 | /**
15 | * A simple class which wraps a float[] needed for one key frame.
16 | */
17 | public class KFAnimationFrame implements HasKeyFrame {
18 |
19 | /**
20 | * The key frame # in the animation sequence.
21 | */
22 | public static final String START_FRAME_JSON_FIELD = "start_frame";
23 | private final int mStartFrame;
24 |
25 | /**
26 | * The values for this key frame.
27 | */
28 | public static final String DATA_JSON_FIELD = "data";
29 | private final float[] mData;
30 |
31 | public static class Builder {
32 | public int startFrame;
33 | public float[] data;
34 |
35 | public KFAnimationFrame build() {
36 | return new KFAnimationFrame(startFrame, data);
37 | }
38 | }
39 |
40 | private KFAnimationFrame(int startFrame, float[] data) {
41 | mStartFrame = startFrame;
42 | mData = ArgCheckUtil.checkArg(
43 | data,
44 | data.length > 0,
45 | DATA_JSON_FIELD);
46 | }
47 |
48 | @Override
49 | public int getKeyFrame() {
50 | return mStartFrame;
51 | }
52 |
53 | public float[] getData() {
54 | return mData;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFAnimationGroup.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | import com.facebook.keyframes.model.keyframedmodels.KeyFramedAnchorPoint;
13 | import com.facebook.keyframes.util.AnimationHelper;
14 | import com.facebook.keyframes.util.ArgCheckUtil;
15 | import com.facebook.keyframes.util.ListHelper;
16 |
17 | import java.util.List;
18 |
19 | /**
20 | * A class representing a single animation layer of this animation.
21 | */
22 | public class KFAnimationGroup {
23 |
24 | /**
25 | * An identifier for this animation layer, in case there are dependencies on it.
26 | */
27 | public static final String GROUP_ID_JSON_FIELD = "group_id";
28 | private final int mGroupId;
29 |
30 | /**
31 | * An identifier for a parent animation layer which this layer is nested in.
32 | */
33 | public static final String PARENT_GROUP_JSON_FIELD = "parent_group";
34 | private final int mParentGroup;
35 |
36 | /**
37 | * The different animations which are part of this layer. This should only include matrix based
38 | * animations since there are no features in this layer.
39 | */
40 | public static final String ANIMATIONS_JSON_FIELD = "animations";
41 | private final List mAnimations;
42 |
43 | private final KFAnimation mAnchorPoint;
44 |
45 | public static class Builder {
46 | public int groupId;
47 | public int parentGroup;
48 | public List animations;
49 |
50 | public KFAnimationGroup build() {
51 | return new KFAnimationGroup(groupId, parentGroup, animations);
52 | }
53 | }
54 |
55 | public KFAnimationGroup(
56 | int groupId,
57 | int parentGroup,
58 | List animations) {
59 | mGroupId = ArgCheckUtil.checkArg(
60 | groupId,
61 | groupId > 0,
62 | GROUP_ID_JSON_FIELD);
63 | mParentGroup = parentGroup;
64 | ListHelper.sort(animations, KFAnimation.ANIMATION_PROPERTY_COMPARATOR);
65 | mAnchorPoint = AnimationHelper.extractSpecialAnimationAnimationSet(
66 | animations,
67 | KFAnimation.PropertyType.ANCHOR_POINT);
68 | mAnimations = ArgCheckUtil.checkArg(
69 | ListHelper.immutableOrEmpty(animations),
70 | animations.size() > 0,
71 | ANIMATIONS_JSON_FIELD);
72 | }
73 |
74 | public int getGroupId() {
75 | return mGroupId;
76 | }
77 |
78 | public int getParentGroup() {
79 | return mParentGroup;
80 | }
81 |
82 | public List getAnimations() {
83 | return mAnimations;
84 | }
85 |
86 | public KeyFramedAnchorPoint getAnchorPoint() {
87 | if (mAnchorPoint == null) {
88 | return null;
89 | }
90 | return (KeyFramedAnchorPoint) mAnchorPoint.getAnimation();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFColorFrame.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | /**
13 | * A simple class which wraps a color value needed for one key frame.
14 | */
15 | public class KFColorFrame implements HasKeyFrame {
16 |
17 | /**
18 | * The key frame # in the animation sequence.
19 | */
20 | public static final String START_FRAME_JSON_FIELD = "start_frame";
21 | private final int mStartFrame;
22 |
23 | /**
24 | * The color value for this key frame.
25 | */
26 | public static final String COLOR_JSON_FIELD = "data";
27 | private final int mColor;
28 |
29 | public static class Builder {
30 | public int startFrame;
31 | public int color;
32 |
33 | public KFColorFrame build() {
34 | return new KFColorFrame(startFrame, color);
35 | }
36 | }
37 |
38 | private KFColorFrame(int startFrame, int color) {
39 | mStartFrame = startFrame;
40 | mColor = color;
41 | }
42 |
43 | @Override
44 | public int getKeyFrame() {
45 | return mStartFrame;
46 | }
47 |
48 | public int getColor() {
49 | return mColor;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFFeatureEffect.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | import com.facebook.keyframes.util.ArgCheckUtil;
13 |
14 | /**
15 | * Describes an effect that can be applied to a feature layer. Currently, only a simple linear
16 | * gradient effect is supported.
17 | */
18 | public class KFFeatureEffect {
19 |
20 | /**
21 | * The object containing gradient and gradient animation information.
22 | */
23 | public static final String GRADIENT_JSON_FIELD = "gradient";
24 | private final KFGradient mGradient;
25 |
26 | public static class Builder {
27 | public KFGradient gradient;
28 |
29 | public KFFeatureEffect build() {
30 | return new KFFeatureEffect(gradient);
31 | }
32 | }
33 |
34 | private KFFeatureEffect(KFGradient gradient) {
35 | mGradient = ArgCheckUtil.checkArg(
36 | gradient,
37 | gradient != null,
38 | GRADIENT_JSON_FIELD);
39 | }
40 |
41 | public KFGradient getGradient() {
42 | return mGradient;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFFeatureFrame.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | import com.facebook.keyframes.KFPath;
16 | import com.facebook.keyframes.util.VectorCommand;
17 | import com.facebook.keyframes.util.ListHelper;
18 |
19 | /**
20 | * A simple class which wraps path command information needed for one key frame.
21 | */
22 | public class KFFeatureFrame implements HasKeyFrame {
23 |
24 | /**
25 | * The key frame # in the animation sequence.
26 | */
27 | public static final String START_FRAME_JSON_FIELD = "start_frame";
28 | private final int mStartFrame;
29 |
30 | /**
31 | * The raw string commands for this feature in one key frame.
32 | */
33 | public static final String DATA_JSON_FIELD = "data";
34 | private final ShapeMoveListData mShapeData;
35 |
36 | public static class Builder {
37 | public int startFrame;
38 | public List data;
39 |
40 | public KFFeatureFrame build() {
41 | return new KFFeatureFrame(startFrame, data);
42 | }
43 | }
44 |
45 | public KFFeatureFrame(int startFrame, List data) {
46 | mStartFrame = startFrame;
47 | mShapeData = new ShapeMoveListData(data);
48 | }
49 |
50 | @Override
51 | public int getKeyFrame() {
52 | return mStartFrame;
53 | }
54 |
55 | public ShapeMoveListData getShapeData() {
56 | return mShapeData;
57 | }
58 |
59 | public static class ShapeMoveListData {
60 |
61 | private final List mVectorCommands;
62 |
63 | public ShapeMoveListData(List data) {
64 | List vectorCommandList = new ArrayList<>();
65 | for (int i = 0, len = data.size(); i < len; i ++) {
66 | vectorCommandList.add(VectorCommand.createVectorCommand(data.get(i)));
67 | }
68 | mVectorCommands = ListHelper.immutableOrEmpty(vectorCommandList);
69 | }
70 |
71 | public void applyFeature(KFPath path) {
72 | for (int i = 0, len = mVectorCommands.size(); i < len; i++) {
73 | mVectorCommands.get(i).apply(path);
74 | }
75 | }
76 |
77 | public List getVectorCommands() {
78 | return mVectorCommands;
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFGradient.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | import com.facebook.keyframes.model.keyframedmodels.KeyFramedGradient;
13 | import com.facebook.keyframes.util.ArgCheckUtil;
14 |
15 | import static com.facebook.keyframes.model.keyframedmodels.KeyFramedGradient.Position.*;
16 |
17 | /**
18 | * An object which wraps information for a gradient effect. Currently only supports simple linear
19 | * gradients.
20 | */
21 | public class KFGradient {
22 |
23 | /**
24 | * To prevent allocating a lot of LinearGradient shaders during animation, and because it looks
25 | * looks like LinearGradient shader params can't be modified after instantiation, we cache the
26 | * shaders needed once at a precision of shaders per second listed here.
27 | */
28 |
29 | /**
30 | * The start color of the gradient, the top color of the linear gradient.
31 | */
32 | public static final String COLOR_START_JSON_FIELD = "color_start";
33 | private final KeyFramedGradient mStartGradient;
34 |
35 | /**
36 | * The end color of the gradient, the bottom color of the linear gradient.
37 | */
38 | public static final String COLOR_END_JSON_FIELD = "color_end";
39 | private final KeyFramedGradient mEndGradient;
40 |
41 | public static class Builder {
42 | public KFGradientColor colorStart;
43 | public KFGradientColor colorEnd;
44 |
45 | public KFGradient build() {
46 | return new KFGradient(colorStart, colorEnd);
47 | }
48 | }
49 |
50 | public KFGradient(KFGradientColor colorStart, KFGradientColor colorEnd) {
51 | mStartGradient = KeyFramedGradient.fromGradient(
52 | ArgCheckUtil.checkArg(
53 | colorStart,
54 | colorStart != null,
55 | COLOR_START_JSON_FIELD),
56 | START);
57 | mEndGradient = KeyFramedGradient.fromGradient(
58 | ArgCheckUtil.checkArg(
59 | colorEnd,
60 | colorEnd != null,
61 | COLOR_END_JSON_FIELD),
62 | END);
63 | }
64 |
65 | public KeyFramedGradient getStartGradient() {
66 | return mStartGradient;
67 | }
68 |
69 | public KeyFramedGradient getEndGradient() {
70 | return mEndGradient;
71 | }
72 | }
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFGradientColor.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | import com.facebook.keyframes.util.ArgCheckUtil;
13 | import com.facebook.keyframes.util.ListHelper;
14 |
15 | import java.util.List;
16 |
17 | /**
18 | * The class which contains information about an animated gradient, including the key frame
19 | * information and timing curves.
20 | */
21 | public class KFGradientColor {
22 |
23 | /**
24 | * A list of {@link KFColorFrame}s, each describing a color given a key frame.
25 | */
26 | public static final String KEY_VALUES_JSON_FIELD = "key_values";
27 | private final List mKeyValues;
28 |
29 | /**
30 | * A list of timing curves which describes how to interpolate between two
31 | * {@link KFColorFrame}s.
32 | */
33 | public static final String TIMING_CURVES_JSON_FIELD = "timing_curves";
34 | private final float[][][] mTimingCurves;
35 |
36 | public static class Builder {
37 | public List keyValues;
38 | public float[][][] timingCurves;
39 |
40 | public KFGradientColor build() {
41 | return new KFGradientColor(keyValues, timingCurves);
42 | }
43 | }
44 |
45 | public KFGradientColor(List keyValues, float[][][] timingCurves) {
46 | mKeyValues = ListHelper.immutableOrEmpty(keyValues);
47 | mTimingCurves = ArgCheckUtil.checkArg(
48 | timingCurves,
49 | ArgCheckUtil.checkTimingCurveObjectValidity(timingCurves, mKeyValues.size()),
50 | TIMING_CURVES_JSON_FIELD);
51 | }
52 |
53 | public List getKeyValues() {
54 | return mKeyValues;
55 | }
56 |
57 | public float[][][] getTimingCurves() {
58 | return mTimingCurves;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/KFImage.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 | package com.facebook.keyframes.model;
10 |
11 | import java.util.List;
12 | import java.util.Map;
13 |
14 | import android.graphics.Bitmap;
15 | import android.graphics.Matrix;
16 | import android.util.SparseArray;
17 |
18 | import com.facebook.keyframes.util.AnimationGroupSort;
19 | import com.facebook.keyframes.util.ArgCheckUtil;
20 | import com.facebook.keyframes.util.ListHelper;
21 |
22 | /**
23 | * The top level model object for one entire animated image. Global information such as frame rate
24 | * it was exported as, frame count, and canvas size are included here for renderers.
25 | */
26 | public class KFImage {
27 |
28 | /**
29 | * The frame rate that this animation was exported at. This is needed to play back the animation
30 | * at the correct speed. It does not limit the playback to discrete frames per second.
31 | */
32 | public static final String FRAME_RATE_JSON_FIELD = "frame_rate";
33 | private final int mFrameRate;
34 |
35 | /**
36 | * The total number of frames for this animation.
37 | */
38 | public static final String FRAME_COUNT_JSON_FIELD = "animation_frame_count";
39 | private final int mFrameCount;
40 |
41 | /**
42 | * A list of all the feature layers for this image.
43 | */
44 | public static final String FEATURES_JSON_FIELD = "features";
45 | private final List mFeatures;
46 |
47 | /**
48 | * A list of all the animation layers for this image.
49 | */
50 | public static final String ANIMATION_GROUPS_JSON_FIELD = "animation_groups";
51 | private final List mAnimationGroups;
52 |
53 | /**
54 | * The canvas size that this image was initially exported as.
55 | */
56 | public static final String CANVAS_SIZE_JSON_FIELD = "canvas_size";
57 | private final float[] mCanvasSize;
58 |
59 | /**
60 | * An optional identification key for this image.
61 | */
62 | public static final String KEY_JSON_FIELD = "key";
63 | private final int mKey;
64 |
65 | /**
66 | * An optional map of bitmaps for this image.
67 | */
68 | public static final String BITMAPS_JSON_FIELD = "bitmaps";
69 | private final Map mBitmaps;
70 |
71 | public static class Builder {
72 | public int frameRate;
73 | public int frameCount;
74 | public List features;
75 | public List animationGroups;
76 | public float[] canvasSize;
77 | public int key;
78 | public Map bitmaps;
79 |
80 | public KFImage build() {
81 | return new KFImage(frameRate, frameCount, features, animationGroups, canvasSize, key, bitmaps);
82 | }
83 | }
84 |
85 | private KFImage(
86 | int frameRate,
87 | int frameCount,
88 | List features,
89 | List animationGroups,
90 | float[] canvasSize,
91 | int key,
92 | Map bitmaps) {
93 | mFrameRate = ArgCheckUtil.checkArg(
94 | frameRate,
95 | frameRate > 0,
96 | FRAME_RATE_JSON_FIELD);
97 | mFrameCount = ArgCheckUtil.checkArg(
98 | frameCount,
99 | frameCount > 0,
100 | FRAME_COUNT_JSON_FIELD);
101 | mFeatures = ArgCheckUtil.checkArg(
102 | ListHelper.immutableOrEmpty(features),
103 | features.size() > 0,
104 | FEATURES_JSON_FIELD);
105 | animationGroups = AnimationGroupSort.sort(animationGroups);
106 | mAnimationGroups =
107 | ArgCheckUtil.checkArg(
108 | ListHelper.immutableOrEmpty(animationGroups),
109 | ArgCheckUtil.checkAnimationGroupIdUniqueness(animationGroups),
110 | ANIMATION_GROUPS_JSON_FIELD);
111 | mCanvasSize = ArgCheckUtil.checkArg(
112 | canvasSize,
113 | canvasSize.length == 2 && canvasSize[0] > 0 && canvasSize[1] > 0,
114 | CANVAS_SIZE_JSON_FIELD);
115 | mKey = key;
116 | mBitmaps = bitmaps;
117 | }
118 |
119 | public int getFrameRate() {
120 | return mFrameRate;
121 | }
122 |
123 | public int getFrameCount() {
124 | return mFrameCount;
125 | }
126 |
127 | public List getFeatures() {
128 | return mFeatures;
129 | }
130 |
131 | public List getAnimationGroups() {
132 | return mAnimationGroups;
133 | }
134 |
135 | /**
136 | * Given a map of group id and corresponding matrices, apply the current matrix state calculated
137 | * from progress in the animation to the matrix in the map.
138 | * @param matrixMap A prefilled map of animation group id -> matrix
139 | * @param frameProgress The progress in animation, given as a frame value
140 | */
141 | public void setAnimationMatrices(SparseArray matrixMap, float frameProgress) {
142 | Matrix matrix;
143 | for (int groupIndex = 0, groupsLen = mAnimationGroups.size();
144 | groupIndex < groupsLen;
145 | groupIndex++) {
146 | KFAnimationGroup group = mAnimationGroups.get(groupIndex);
147 | matrix = matrixMap.get(group.getGroupId());
148 | matrix.reset();
149 | if (group.getAnchorPoint() != null) {
150 | group.getAnchorPoint().apply(frameProgress, matrix);
151 | }
152 | for (int animationIndex = 0, animationsLen = group.getAnimations().size();
153 | animationIndex < animationsLen;
154 | animationIndex++) {
155 | group.getAnimations().get(animationIndex).getAnimation().apply(frameProgress, matrix);
156 | }
157 | if (group.getParentGroup() > 0) {
158 | matrix.postConcat(matrixMap.get(group.getParentGroup()));
159 | }
160 | }
161 | }
162 |
163 | public float[] getCanvasSize() {
164 | return mCanvasSize;
165 | }
166 |
167 | public int getKey() {
168 | return mKey;
169 | }
170 |
171 | public Map getBitmaps() {
172 | return mBitmaps;
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/keyframedmodels/KeyFrameAnimationHelper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model.keyframedmodels;
11 |
12 | import java.util.ArrayList;
13 | import java.util.Collections;
14 | import java.util.List;
15 |
16 | import android.view.animation.Interpolator;
17 |
18 | import com.facebook.keyframes.util.KFPathInterpolator;
19 |
20 | /**
21 | * A helper class to build a list of interpolators corresponding to a list of timing curves, for use
22 | * with key framed animations to describe interpolation between key frames.
23 | */
24 | public class KeyFrameAnimationHelper {
25 |
26 | /**
27 | * Given a list of timing curves consisting of an outTangent and an inTangent with x/y values,
28 | * returns an ImmutableList with a corresponding interpolator for each timing curve, in the same
29 | * order as supplied.
30 | */
31 | public static List buildInterpolatorList(float[][][] timingCurves) {
32 | if (timingCurves == null) {
33 | return Collections.emptyList();
34 | }
35 | List interpolatorList = new ArrayList<>();
36 | for (int i = 0, len = timingCurves.length; i < len; i++) {
37 | float[][] influences = timingCurves[i];
38 | interpolatorList.add(
39 | new KFPathInterpolator(
40 | influences[0][0],
41 | influences[0][1],
42 | influences[1][0],
43 | influences[1][1]));
44 | }
45 | return Collections.unmodifiableList(interpolatorList);
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/keyframedmodels/KeyFramedAnchorPoint.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model.keyframedmodels;
11 |
12 | import android.graphics.Matrix;
13 |
14 | import com.facebook.keyframes.model.HasKeyFrame;
15 | import com.facebook.keyframes.model.KFAnimation;
16 | import com.facebook.keyframes.model.KFAnimationFrame;
17 |
18 | import java.util.List;
19 |
20 | /**
21 | * A special object that defines the anchor point for the other animations in a group or feature.
22 | */
23 | public class KeyFramedAnchorPoint extends KeyFramedObject {
24 |
25 | public static KeyFramedAnchorPoint fromAnchorPoint(KFAnimation animation) {
26 | return new KeyFramedAnchorPoint(animation.getAnimationFrames(), animation.getTimingCurves());
27 | }
28 |
29 | private KeyFramedAnchorPoint(
30 | List objects,
31 | float[][][] timingCurves) {
32 | super(objects, timingCurves);
33 | }
34 |
35 | @Override
36 | protected void applyImpl(
37 | KFAnimationFrame stateA,
38 | KFAnimationFrame stateB,
39 | float interpolationValue,
40 | Matrix matrix) {
41 | if (stateB == null) {
42 | matrix.postTranslate(-stateA.getData()[0], -stateA.getData()[1]);
43 | return;
44 | }
45 | matrix.postTranslate(
46 | -interpolateValue(stateA.getData()[0], stateB.getData()[0], interpolationValue),
47 | -interpolateValue(stateA.getData()[1], stateB.getData()[1], interpolationValue));
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/keyframedmodels/KeyFramedGradient.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model.keyframedmodels;
11 |
12 | import java.util.List;
13 |
14 | import android.graphics.Color;
15 |
16 | import com.facebook.keyframes.model.KFColorFrame;
17 | import com.facebook.keyframes.model.KFGradientColor;
18 |
19 | /**
20 | * A {link KeyFramedObject} which houses information for a gradient animation. This includes just
21 | * start and end colors. The modifiable class is a simple container object that wraps a start and
22 | * end color that can be modified and passed around. This is a post-process object used for
23 | * KFGradientColor.
24 | */
25 | public class KeyFramedGradient
26 | extends KeyFramedObject {
27 |
28 | /**
29 | * An enum for whether this gradient color is for the start (top) or end (bottom) color.
30 | */
31 | public enum Position {
32 | START,
33 | END
34 | }
35 |
36 | /**
37 | * A simple container that includes a start and end color which can be passed around and modified.
38 | * The setter methods can only be called by this class, since all other classes should only need
39 | * to grab values from the container.
40 | */
41 | public static class GradientColorPair {
42 | private int mStartColor;
43 | private int mEndColor;
44 |
45 | public int getStartColor() {
46 | return mStartColor;
47 | }
48 |
49 | private void setStartColor(int startColor) {
50 | mStartColor = startColor;
51 | }
52 |
53 | public int getEndColor() {
54 | return mEndColor;
55 | }
56 |
57 | private void setEndColor(int endColor) {
58 | mEndColor = endColor;
59 | }
60 | }
61 |
62 | /**
63 | * Constructs a KeyFramedGradient from a {@link KFGradientColor}.
64 | * @param gradientColor The corresponding {@link KFGradientColor}
65 | * @param position The position of this color, either START or END
66 | */
67 | public static KeyFramedGradient fromGradient(
68 | KFGradientColor gradientColor,
69 | Position position) {
70 | return new KeyFramedGradient(
71 | gradientColor.getKeyValues(),
72 | gradientColor.getTimingCurves(),
73 | position);
74 | }
75 |
76 | private final Position mPosition;
77 |
78 | private KeyFramedGradient(
79 | List objects,
80 | float[][][] timingCurves,
81 | Position position) {
82 | super(objects, timingCurves);
83 | mPosition = position;
84 | }
85 |
86 | /**
87 | * Applies the current state, given by the interpolationValue, to the {@link GradientColorPair}.
88 | * @param stateA Initial state
89 | * @param stateB End state
90 | * @param interpolationValue Progress [0..1] between stateA and stateB
91 | * @param modifiable The {@link GradientColorPair} to apply the values to
92 | */
93 | @Override
94 | protected void applyImpl(
95 | KFColorFrame stateA,
96 | KFColorFrame stateB,
97 | float interpolationValue,
98 | GradientColorPair modifiable) {
99 | if (stateB == null) {
100 | if (mPosition == Position.START) {
101 | modifiable.setStartColor(stateA.getColor());
102 | } else {
103 | modifiable.setEndColor(stateA.getColor());
104 | }
105 | return;
106 | }
107 | if (mPosition == Position.START) {
108 | modifiable.setStartColor(
109 | getTransitionColor(interpolationValue, stateA.getColor(), stateB.getColor()));
110 | } else {
111 | modifiable.setEndColor(
112 | getTransitionColor(interpolationValue, stateA.getColor(), stateB.getColor()));
113 | }
114 | }
115 |
116 | /**
117 | * Given a start and end color, as well as a transition progress between the two, return a color
118 | * which is in between colorA and colorB, where 0 progress is colorA, 1 progress is colorB, and
119 | * all other progress values in between describe a linear transition.
120 | * @param progress Progress [0..1] between colorA and colorB.
121 | * @param colorA The color associated with progress 0
122 | * @param colorB The color associated with progress 1
123 | * @return The intermediate color that is {progress} between {colorA} and {colorB}.
124 | */
125 | public static int getTransitionColor(float progress, int colorA, int colorB) {
126 | int startA = Color.alpha(colorA);
127 | int startR = Color.red(colorA);
128 | int startG = Color.green(colorA);
129 | int startB = Color.blue(colorA);
130 |
131 | int endA = Color.alpha(colorB);
132 | int endR = Color.red(colorB);
133 | int endG = Color.green(colorB);
134 | int endB = Color.blue(colorB);
135 | return ((startA + (int) (progress * (endA - startA))) << 24) |
136 | ((startR + (int) (progress * (endR - startR))) << 16) |
137 | ((startG + (int) (progress * (endG - startG))) << 8) |
138 | ((startB + (int) (progress * (endB - startB))));
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/keyframedmodels/KeyFramedMatrixAnimation.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model.keyframedmodels;
11 |
12 | import android.graphics.Matrix;
13 | import com.facebook.keyframes.model.KFAnimation;
14 | import com.facebook.keyframes.model.KFAnimationFrame;
15 |
16 | import java.util.List;
17 |
18 | /**
19 | * A {@link KeyFramedObject} which houses information for a matrix based animation. This includes
20 | * rotation, scale, and translation (position) information which can be applied to other animation
21 | * layers or feature layers. This is a post-process object used for {@link KFAnimation}.
22 | */
23 | public class KeyFramedMatrixAnimation extends KeyFramedObject {
24 |
25 | /**
26 | * Constructs a KeyFramedMatrixAnimation from a {@link KFAnimation}.
27 | */
28 | public static KeyFramedMatrixAnimation fromAnimation(KFAnimation animation) {
29 | if (!animation.getPropertyType().isMatrixBased()) {
30 | throw new IllegalArgumentException(
31 | "Cannot create a KeyFramedMatrixAnimation from a non matrix based KFAnimation.");
32 | }
33 | return new KeyFramedMatrixAnimation(
34 | animation.getAnimationFrames(),
35 | animation.getTimingCurves(),
36 | animation.getPropertyType(),
37 | animation.getAnchor());
38 | }
39 |
40 | /**
41 | * The property type that is animated by this animation.
42 | */
43 | private final KFAnimation.PropertyType mPropertyType;
44 |
45 | /**
46 | * The origin for this matrix animation.
47 | * Deprecated in favor of the ANCHOR_POINT animation.
48 | */
49 | @Deprecated
50 | private final float[] mAnchor;
51 |
52 | private KeyFramedMatrixAnimation(
53 | List objects,
54 | float[][][] timingCurves,
55 | KFAnimation.PropertyType propertyType,
56 | float[] anchor) {
57 | super(objects, timingCurves);
58 | mPropertyType = propertyType;
59 |
60 | // Deprecated anchor behavior below
61 | mAnchor = anchor != null ? anchor : new float[2];
62 | if (propertyType == KFAnimation.PropertyType.POSITION) {
63 | // Translations are special cased relative transforms.
64 | mAnchor[0] = objects.get(0).getData()[0];
65 | mAnchor[1] = objects.get(0).getData()[1];
66 | }
67 | }
68 |
69 | /**
70 | * Applies the current state, given by interpolationValue, to the Matrix object. Implementation
71 | * of the application method depends on the {@link KFAnimation.PropertyType} for this
72 | * animation.
73 | * @param stateA Initial state
74 | * @param stateB End state
75 | * @param interpolationValue Progress [0..1] between stateA and stateB
76 | * @param modifiable The matrix to apply the values to
77 | */
78 | @Override
79 | protected void applyImpl(
80 | KFAnimationFrame stateA,
81 | KFAnimationFrame stateB,
82 | float interpolationValue,
83 | Matrix modifiable) {
84 | switch (mPropertyType) {
85 | case ROTATION:
86 | applyRotation(stateA, stateB, interpolationValue, modifiable);
87 | break;
88 | case SCALE:
89 | applyScale(stateA, stateB, interpolationValue, modifiable);
90 | break;
91 | case POSITION:
92 | applyPosition(stateA, stateB, interpolationValue, modifiable);
93 | break;
94 | case X_POSITION:
95 | applyXPosition(stateA, stateB, interpolationValue, modifiable);
96 | break;
97 | case Y_POSITION:
98 | applyYPosition(stateA, stateB, interpolationValue, modifiable);
99 | break;
100 | default:
101 | throw new UnsupportedOperationException(
102 | "Cannot apply matrix transformation to " + mPropertyType);
103 | }
104 | }
105 |
106 | /**
107 | * This method applies a rotational transform to the matrix, interpolated between two states.
108 | */
109 | private void applyRotation(
110 | KFAnimationFrame stateA,
111 | KFAnimationFrame stateB,
112 | float interpolationValue,
113 | Matrix modifiable) {
114 | if (stateB == null) {
115 | modifiable.postRotate(stateA.getData()[0],
116 | mAnchor != null ? mAnchor[0] : 0,
117 | mAnchor != null ? mAnchor[1] : 0);
118 | return;
119 | }
120 | float rotationStart = stateA.getData()[0];
121 | float rotationEnd = stateB.getData()[0];
122 | modifiable.postRotate(
123 | interpolateValue(rotationStart, rotationEnd, interpolationValue),
124 | mAnchor != null ? mAnchor[0] : 0,
125 | mAnchor != null ? mAnchor[1] : 0);
126 | }
127 |
128 | /**
129 | * This method applies a scale transformation to the matrix, interpolated between two states.
130 | */
131 | private void applyScale(
132 | KFAnimationFrame stateA,
133 | KFAnimationFrame stateB,
134 | float interpolationValue,
135 | Matrix modifiable) {
136 | if (stateB == null) {
137 | modifiable.postScale(
138 | stateA.getData()[0] / 100f,
139 | stateA.getData()[1] / 100f,
140 | mAnchor != null ? mAnchor[0] : 0,
141 | mAnchor != null ? mAnchor[1] : 0);
142 | return;
143 | }
144 | float scaleStartX = stateA.getData()[0];
145 | float scaleEndX = stateB.getData()[0];
146 | float scaleStartY = stateA.getData()[1];
147 | float scaleEndY = stateB.getData()[1];
148 | modifiable.postScale(
149 | interpolateValue(scaleStartX, scaleEndX, interpolationValue) / 100f,
150 | interpolateValue(scaleStartY, scaleEndY, interpolationValue) / 100f,
151 | mAnchor != null ? mAnchor[0] : 0,
152 | mAnchor != null ? mAnchor[1] : 0);
153 | }
154 |
155 | /**
156 | * This method applies an X translation transformation to the matrix, interpolated between two
157 | * states.
158 | */
159 | private void applyXPosition(
160 | KFAnimationFrame stateA,
161 | KFAnimationFrame stateB,
162 | float interpolationValue,
163 | Matrix modifiable) {
164 | if (stateB == null) {
165 | modifiable.postTranslate(stateA.getData()[0], 0);
166 | return;
167 | }
168 | float translationStartX = stateA.getData()[0];
169 | float translationEndX = stateB.getData()[0];
170 | modifiable.postTranslate(
171 | interpolateValue(translationStartX, translationEndX, interpolationValue),
172 | 0);
173 | }
174 |
175 | /**
176 | * This method applies a Y translation transformation to the matrix, interpolated between two
177 | * states.
178 | */
179 | private void applyYPosition(
180 | KFAnimationFrame stateA,
181 | KFAnimationFrame stateB,
182 | float interpolationValue,
183 | Matrix modifiable) {
184 | if (stateB == null) {
185 | modifiable.postTranslate(0, stateA.getData()[0]);
186 | return;
187 | }
188 | float translationStartY = stateA.getData()[0];
189 | float translationEndY = stateB.getData()[0];
190 | modifiable.postTranslate(
191 | 0,
192 | interpolateValue(translationStartY, translationEndY, interpolationValue));
193 | }
194 |
195 | /**
196 | * This method applies a translation transformation to the matrix. Anchor points for a
197 | * translation animation are special cased to be relative to the position of the first frame
198 | * of this animation. This means that if the translation for the first frame is 80x, 80y, and the
199 | * translation for the second key frame is 90x, 70y, the resulting translation is 10x, -10y.
200 | *
201 | * Deprecated in favor of X_POSITION and Y_POSITION transforms.
202 | */
203 | @Deprecated
204 | private void applyPosition(
205 | KFAnimationFrame stateA,
206 | KFAnimationFrame stateB,
207 | float interpolationValue,
208 | Matrix modifiable) {
209 | if (stateB == null) {
210 | return;
211 | }
212 | float translationStartX = stateA.getData()[0];
213 | float translationEndX = stateB.getData()[0];
214 | float translationStartY = stateA.getData()[1];
215 | float translationEndY = stateB.getData()[1];
216 | modifiable.postTranslate(
217 | interpolateValue(translationStartX, translationEndX, interpolationValue) - mAnchor[0],
218 | interpolateValue(translationStartY, translationEndY, interpolationValue) - mAnchor[1]);
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/keyframedmodels/KeyFramedObject.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model.keyframedmodels;
11 |
12 | import android.util.SparseArray;
13 | import android.view.animation.Interpolator;
14 | import com.facebook.keyframes.model.HasKeyFrame;
15 |
16 | import java.util.List;
17 |
18 | /**
19 | * A generic object which holds key framed data and how to process information given a frame
20 | * progress. This object contains a generic method to apply properties of this object to a
21 | * modifyable object M (e.g. Path / Matrix / Generic Container) given a frame progress.
22 | * @param The type of animated object held by this KeyFramedObject
23 | * @param A modifiable object passed into the apply function to
24 | */
25 | public abstract class KeyFramedObject {
26 |
27 | private final SparseArray mObjects;
28 | private final List mInterpolators;
29 | private final int mFirstDescribedFrame;
30 | private final int mLastDescribedFrame;
31 |
32 | public KeyFramedObject(List objects, float[][][] timingCurves) {
33 | int listSize = objects.size();
34 | mObjects = new SparseArray<>(listSize);
35 | T object;
36 | for (int i = 0; i < listSize; i++) {
37 | object = objects.get(i);
38 | mObjects.put(object.getKeyFrame(), object);
39 | }
40 |
41 | mFirstDescribedFrame = mObjects.keyAt(0);
42 | mLastDescribedFrame = mObjects.keyAt(listSize - 1);
43 | mInterpolators = KeyFrameAnimationHelper.buildInterpolatorList(timingCurves);
44 | }
45 |
46 | /**
47 | * Constructor for creating empty/invalid KeyFramedObjects.
48 | */
49 | protected KeyFramedObject() {
50 | mObjects = null;
51 | mInterpolators = null;
52 | mFirstDescribedFrame = 0;
53 | mLastDescribedFrame = 0;
54 | }
55 |
56 | /**
57 | * Applies the corresponding values determined by frameProgress to the modifiable object. This
58 | * method does some simple calculations to determine which key frames to use, as well as the
59 | * interpolation value between them based on the timing curve information, then delegates to
60 | * {@link #applyImpl(HasKeyFrame, HasKeyFrame, float, Object)} for the actual application to
61 | * the modifiable object.
62 | * @param frameProgress The progress, described in frames, of the animation.
63 | * @param modifiable The object to insert values into.
64 | */
65 | public void apply(float frameProgress, M modifiable) {
66 | if (mInterpolators.isEmpty() ||
67 | frameProgress <= mFirstDescribedFrame) {
68 | applyImpl(mObjects.get(mFirstDescribedFrame), null, 0, modifiable);
69 | return;
70 | }
71 | if (frameProgress >= mLastDescribedFrame) {
72 | applyImpl(mObjects.get(mLastDescribedFrame), null, 0, modifiable);
73 | return;
74 | }
75 | T thisFrame = null;
76 | T nextFrame = null;
77 | int interpolatorIndex;
78 | int len = mInterpolators.size();
79 | for (interpolatorIndex = 0; interpolatorIndex < len; interpolatorIndex++) {
80 | if (mObjects.keyAt(interpolatorIndex) == frameProgress ||
81 | (mObjects.keyAt(interpolatorIndex) < frameProgress &&
82 | mObjects.keyAt(interpolatorIndex + 1) > frameProgress)) {
83 | thisFrame = mObjects.valueAt(interpolatorIndex);
84 | nextFrame = mObjects.valueAt(interpolatorIndex + 1);
85 | break;
86 | }
87 | }
88 | float progress = (frameProgress - thisFrame.getKeyFrame()) /
89 | (nextFrame.getKeyFrame() - thisFrame.getKeyFrame());
90 | applyImpl(
91 | thisFrame,
92 | nextFrame,
93 | mInterpolators.get(interpolatorIndex).getInterpolation(progress),
94 | modifiable);
95 | }
96 |
97 | /**
98 | * Apply the given state to a modifiable.
99 | * @param stateA Initial state
100 | * @param stateB End state
101 | * @param interpolationValue Progress [0..1] between stateA and stateB
102 | * @param modifiable The modifiable object to apply the values to
103 | */
104 | protected abstract void applyImpl(
105 | T stateA,
106 | T stateB,
107 | float interpolationValue,
108 | M modifiable);
109 |
110 | /**
111 | * Given two values and the progress from valueA to valueB, returns the transitional value in
112 | * between.
113 | */
114 | protected static float interpolateValue(float valueA, float valueB, float progress) {
115 | return valueA + (valueB - valueA) * progress;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/keyframedmodels/KeyFramedOpacity.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model.keyframedmodels;
11 |
12 | import java.util.List;
13 |
14 | import com.facebook.keyframes.model.KFAnimation;
15 | import com.facebook.keyframes.model.KFAnimation.PropertyType;
16 | import com.facebook.keyframes.model.KFAnimationFrame;
17 |
18 |
19 | /**
20 | * This is a special cased KFAnimation, since it is the only animation which is not applied
21 | * via a matrix. The information for this Modifiable is packed into a single length array.
22 | * A {@link KeyFramedObject} which houses information about a stroke width animation. This includes
23 | * float values for stroke width at any given key frame. This is a post-process object used for
24 | * KFAnimation.
25 | */
26 | public class KeyFramedOpacity
27 | extends KeyFramedObject {
28 |
29 | /**
30 | * A container object so that this class can set values on an object which on a common reference.
31 | */
32 | public static class Opacity {
33 | private float mOpacity = 100;
34 |
35 | public float getOpacity() {
36 | return mOpacity;
37 | }
38 |
39 | public void setOpacity(float opacity) {
40 | mOpacity = opacity;
41 | }
42 | }
43 |
44 | /**
45 | * Constructs a KeyFramedOpacity from a {@link KFAnimation}.
46 | */
47 | public static KeyFramedOpacity fromAnimation(KFAnimation animation) {
48 | if (animation.getPropertyType() != KFAnimation.PropertyType.OPACITY) {
49 | throw new IllegalArgumentException(
50 | "Cannot create a KeyFramedOpacity object from a non OPACITY animation.");
51 | }
52 | return new KeyFramedOpacity(animation.getAnimationFrames(), animation.getTimingCurves());
53 | }
54 |
55 | public KeyFramedOpacity(
56 | List objects,
57 | float[][][] timingCurves) {
58 | super(objects, timingCurves);
59 | }
60 |
61 | private KeyFramedOpacity() {
62 | super();
63 | }
64 |
65 | /**
66 | * Applies the current state, given by interpolationValue, to the Opacity object.
67 | * @param stateA Initial state
68 | * @param stateB End state
69 | * @param interpolationValue Progress [0..1] between stateA and stateB
70 | * @param modifiable The Opacity to apply the values to
71 | */
72 | @Override
73 | protected void applyImpl(
74 | KFAnimationFrame stateA,
75 | KFAnimationFrame stateB,
76 | float interpolationValue,
77 | Opacity modifiable) {
78 | if (stateB == null) {
79 | modifiable.setOpacity(stateA.getData()[0]);
80 | return;
81 | }
82 | modifiable.setOpacity(
83 | interpolateValue(stateA.getData()[0], stateB.getData()[0], interpolationValue));
84 | }
85 | }
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/keyframedmodels/KeyFramedPath.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model.keyframedmodels;
11 |
12 | import java.util.List;
13 |
14 | import com.facebook.keyframes.KFPath;
15 | import com.facebook.keyframes.model.KFFeature;
16 | import com.facebook.keyframes.model.KFFeatureFrame;
17 |
18 | /**
19 | * A {@link KeyFramedObject} which houses information for a key framed feature object. This
20 | * includes the commands to draw a shape at each given key frame. This is a post-process object
21 | * used for KFFeature.
22 | */
23 | public class KeyFramedPath extends KeyFramedObject {
24 |
25 | /**
26 | * Constructs a KeyFramedPath from a {@link KFFeature}.
27 | */
28 | public static KeyFramedPath fromFeature(KFFeature feature) {
29 | return new KeyFramedPath(feature.getKeyFrames(), feature.getTimingCurves());
30 | }
31 |
32 | private KeyFramedPath(List featureFrames, float[][][] timingCurves) {
33 | super(featureFrames, timingCurves);
34 | }
35 |
36 | /**
37 | * Applies the current state, given by interpolationValue, to the supplied Path object.
38 | * @param stateA Initial state
39 | * @param stateB End state
40 | * @param interpolationValue Progress [0..1] between stateA and stateB
41 | * @param modifiable The modifiable object to apply the values to
42 | */
43 | @Override
44 | protected void applyImpl(
45 | KFFeatureFrame stateA,
46 | KFFeatureFrame stateB,
47 | float interpolationValue,
48 | KFPath modifiable) {
49 | if (stateB == null || interpolationValue == 0) {
50 | stateA.getShapeData().applyFeature(modifiable);
51 | return;
52 | }
53 | KFFeatureFrame.ShapeMoveListData thisMoveList = stateA.getShapeData();
54 | KFFeatureFrame.ShapeMoveListData nextMoveList = stateB.getShapeData();
55 | for (int i = 0, len = thisMoveList.getVectorCommands().size(); i < len; i++) {
56 | thisMoveList.getVectorCommands().get(i).interpolate(
57 | nextMoveList.getVectorCommands().get(i),
58 | interpolationValue,
59 | modifiable);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/model/keyframedmodels/KeyFramedStrokeWidth.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model.keyframedmodels;
11 |
12 | import java.util.List;
13 |
14 | import com.facebook.keyframes.model.KFAnimation;
15 | import com.facebook.keyframes.model.KFAnimationFrame;
16 |
17 |
18 | /**
19 | * This is a special cased KFAnimation, since it is the only animation which is not applied
20 | * via a matrix. The information for this Modifiable is packed into a single length array.
21 | * A {@link KeyFramedObject} which houses information about a stroke width animation. This includes
22 | * float values for stroke width at any given key frame. This is a post-process object used for
23 | * KFAnimation.
24 | */
25 | public class KeyFramedStrokeWidth
26 | extends KeyFramedObject {
27 |
28 | /**
29 | * A container object so that this class can set values on an object which on a common reference.
30 | */
31 | public static class StrokeWidth {
32 | private float mStrokeWidth;
33 |
34 | public float getStrokeWidth() {
35 | return Math.abs(mStrokeWidth);
36 | }
37 |
38 | public void setStrokeWidth(float strokeWidth) {
39 | mStrokeWidth = strokeWidth;
40 | }
41 |
42 | public void adjustScale(float scale) {
43 | mStrokeWidth *= scale;
44 | }
45 | }
46 |
47 | /**
48 | * Constructs a KeyFramedStrokeWidth from a {@link KFAnimation}.
49 | */
50 | public static KeyFramedStrokeWidth fromAnimation(KFAnimation animation) {
51 | if (animation.getPropertyType() != KFAnimation.PropertyType.STROKE_WIDTH) {
52 | throw new IllegalArgumentException(
53 | "Cannot create a KeyFramedStrokeWidth object from a non STROKE_WIDTH animation.");
54 | }
55 | return new KeyFramedStrokeWidth(animation.getAnimationFrames(), animation.getTimingCurves());
56 | }
57 |
58 | public KeyFramedStrokeWidth(
59 | List objects,
60 | float[][][] timingCurves) {
61 | super(objects, timingCurves);
62 | }
63 |
64 | private KeyFramedStrokeWidth() {
65 | super();
66 | }
67 |
68 | /**
69 | * Applies the current state, given by interpolationValue, to the StrokeWidth object.
70 | * @param stateA Initial state
71 | * @param stateB End state
72 | * @param interpolationValue Progress [0..1] between stateA and stateB
73 | * @param modifiable The StrokeWidth to apply the values to
74 | */
75 | @Override
76 | protected void applyImpl(
77 | KFAnimationFrame stateA,
78 | KFAnimationFrame stateB,
79 | float interpolationValue,
80 | StrokeWidth modifiable) {
81 | if (stateB == null) {
82 | modifiable.setStrokeWidth(stateA.getData()[0]);
83 | return;
84 | }
85 | modifiable.setStrokeWidth(
86 | interpolateValue(stateA.getData()[0], stateB.getData()[0], interpolationValue));
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/util/AnimationGroupSort.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.util;
11 |
12 | import com.facebook.keyframes.model.KFAnimationGroup;
13 |
14 | import java.util.ArrayList;
15 | import java.util.Collections;
16 | import java.util.Iterator;
17 | import java.util.List;
18 | import java.util.Stack;
19 |
20 | public class AnimationGroupSort {
21 |
22 | /**
23 | * Sorts a list of {@link KFAnimationGroup} in topological order. The list passed in should be
24 | * mutable, and will be destroyed in the process.
25 | */
26 | public static List sort(List animationGroupList) {
27 | if (animationGroupList == null || animationGroupList.isEmpty()) {
28 | return Collections.EMPTY_LIST;
29 | }
30 | List result = new ArrayList<>();
31 | Stack rootNodes = new Stack<>();
32 |
33 | KFAnimationGroup currGroup;
34 | for (Iterator iterator = animationGroupList.iterator();
35 | iterator.hasNext();) {
36 | currGroup = iterator.next();
37 | if (currGroup.getParentGroup() == 0) {
38 | rootNodes.push(currGroup);
39 | iterator.remove();
40 | }
41 | }
42 |
43 | while (!rootNodes.isEmpty()) {
44 | currGroup = rootNodes.pop();
45 | result.add(currGroup);
46 | for (Iterator iterator = animationGroupList.iterator();
47 | iterator.hasNext();) {
48 | KFAnimationGroup childGroup = iterator.next();
49 | if (childGroup.getParentGroup() == currGroup.getGroupId()) {
50 | rootNodes.push(childGroup);
51 | }
52 | }
53 | }
54 | return result;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/util/AnimationHelper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.util;
11 |
12 | import com.facebook.keyframes.model.KFAnimation;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * A helper class for parsing and extracting information from various KFAnimations.
18 | */
19 | public class AnimationHelper {
20 |
21 | /**
22 | * Returns a special PropertyType KeyframeAnimation, if available. This method modifies the list
23 | * passed in by found entry from the list.
24 | * @param animations The complete list of feature animations to extract the animation from
25 | * @return a valid animation of the passed in type, if found, or null otherwise
26 | */
27 | public static KFAnimation extractSpecialAnimationAnimationSet(
28 | List animations,
29 | KFAnimation.PropertyType specialAnimationType) {
30 | if (animations == null) {
31 | return null;
32 | }
33 | int specialAnimationIndex = -1;
34 | for (int i = 0, len = animations.size(); i < len; i++) {
35 | if (animations.get(i).getPropertyType() == specialAnimationType) {
36 | // Only case is a stroke width animation, special to feature animation set. Remove from the
37 | // set of matrix based animations and remember the index.
38 | specialAnimationIndex = i;
39 | break;
40 | }
41 | }
42 | if (specialAnimationIndex == -1) {
43 | return null;
44 | }
45 | return animations.remove(specialAnimationIndex);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/util/ArgCheckUtil.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.util;
11 |
12 | import com.facebook.keyframes.model.KFAnimationGroup;
13 |
14 | import java.util.HashSet;
15 | import java.util.List;
16 | import java.util.Locale;
17 | import java.util.Set;
18 |
19 | public class ArgCheckUtil {
20 |
21 | /**
22 | * Checks the validity of an argument, given a condition. If the condition passes, the argument
23 | * is returned. If not, an IllegalArgumentException is thrown.
24 | * @param arg The argument
25 | * @param condition The evaluated condition
26 | * @param argName The name of the argument field for use in the exception, if needed.
27 | * @return The argument, if condition is valid.
28 | * @throws IllegalArgumentException if the condition is invalid
29 | */
30 | public static T checkArg(T arg, boolean condition, String argName) {
31 | if (condition) {
32 | return arg;
33 | }
34 | throw new IllegalArgumentException(
35 | String.format(Locale.US,
36 | "Illegal argument for %s.",
37 | argName));
38 | }
39 |
40 | /**
41 | * Checks that the format of a timing curve 3D float array is valid. The number of timing curves
42 | * for an animation should be equal to the number of keyframes - 1.
43 | * @param timingCurves the 3D float array to check
44 | * @param keyFrameQuantity the number of key frames this animation has
45 | * @return true if the format is valid, false otherwise
46 | */
47 | public static boolean checkTimingCurveObjectValidity(
48 | float[][][] timingCurves,
49 | int keyFrameQuantity) {
50 | if (keyFrameQuantity <= 1 && (timingCurves == null || timingCurves.length == 0)) {
51 | return true;
52 | }
53 | if (keyFrameQuantity - 1 != timingCurves.length) {
54 | return false;
55 | }
56 | for (int i = 0; i < timingCurves.length; i++) {
57 | if (timingCurves[i].length != 2) {
58 | return false;
59 | }
60 | for (int j = 0; j < timingCurves[i].length; j++) {
61 | if (timingCurves[i][j].length != 2) {
62 | return false;
63 | }
64 | }
65 | }
66 | return true;
67 | }
68 |
69 | /**
70 | * Checks that the list of {@link KFAnimationGroup} all contain unique IDs.
71 | * @param groups the list of {@link KFAnimationGroup}
72 | * @return true if all IDs are unique, false otherwise
73 | */
74 | public static boolean checkAnimationGroupIdUniqueness(List groups) {
75 | if (groups == null || groups.size() == 0) {
76 | return true;
77 | }
78 | Set keys = new HashSet<>(groups.size());
79 | for (int i = 0, len = groups.size(); i < len; i++) {
80 | Integer groupId = groups.get(i).getGroupId();
81 | if (keys.contains(groupId)) {
82 | return false;
83 | }
84 | keys.add(groupId);
85 | }
86 | return true;
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/util/KFPathInterpolator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2013 The Android Open Source Project
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.facebook.keyframes.util;
18 |
19 | import android.graphics.Path;
20 | import android.graphics.PathMeasure;
21 | import android.view.animation.Interpolator;
22 |
23 | /**
24 | * This class is directly lifted from the compat library's
25 | * {@link android.support.v4.view.animation.PathInterpolatorCompat}. The reason we need this class
26 | * is to be able to control the precision ourselves for a smaller memory footprint. The normal
27 | * PathInterpolator allocates ~1400 floats every time and holds on to this in memory for the entire
28 | * lifetime of the object, while this one allocates only 10% of that. We dont need high precision
29 | * for what we are using this for.
30 | */
31 | public class KFPathInterpolator implements Interpolator {
32 | /**
33 | * Governs the accuracy of the approximation of the {@link Path}.
34 | */
35 | private static final float PRECISION = 0.03f;
36 | private final float[] mX;
37 | private final float[] mY;
38 |
39 | public KFPathInterpolator(float controlX1, float controlY1, float controlX2, float controlY2) {
40 | Path path = new Path();
41 | path.moveTo(0, 0);
42 | path.cubicTo(controlX1, controlY1, controlX2, controlY2, 1f, 1f);
43 | final PathMeasure pathMeasure = new PathMeasure(path, false /* forceClosed */);
44 | final float pathLength = pathMeasure.getLength();
45 | final int numPoints = (int) (pathLength / PRECISION) + 1;
46 | mX = new float[numPoints];
47 | mY = new float[numPoints];
48 | final float[] position = new float[2];
49 | for (int i = 0; i < numPoints; ++i) {
50 | final float distance = (i * pathLength) / (numPoints - 1);
51 | pathMeasure.getPosTan(distance, position, null /* tangent */);
52 | mX[i] = position[0];
53 | mY[i] = position[1];
54 | }
55 | }
56 |
57 | @Override
58 | public float getInterpolation(float t) {
59 | if (t <= 0.0f) {
60 | return 0.0f;
61 | } else if (t >= 1.0f) {
62 | return 1.0f;
63 | }
64 | // Do a binary search for the correct x to interpolate between.
65 | int startIndex = 0;
66 | int endIndex = mX.length - 1;
67 | while (endIndex - startIndex > 1) {
68 | int midIndex = (startIndex + endIndex) / 2;
69 | if (t < mX[midIndex]) {
70 | endIndex = midIndex;
71 | } else {
72 | startIndex = midIndex;
73 | }
74 | }
75 | final float xRange = mX[endIndex] - mX[startIndex];
76 | if (xRange == 0) {
77 | return mY[startIndex];
78 | }
79 | final float tInRange = t - mX[startIndex];
80 | final float fraction = tInRange / xRange;
81 | final float startY = mY[startIndex];
82 | final float endY = mY[endIndex];
83 | return startY + (fraction * (endY - startY));
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/keyframe/src/main/java/com/facebook/keyframes/util/ListHelper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.util;
11 |
12 | import java.util.Collections;
13 | import java.util.Comparator;
14 | import java.util.List;
15 |
16 | public class ListHelper {
17 |
18 | /**
19 | * Sorts a list, if it exists. Otherwise just returns.
20 | */
21 | public static void sort(List list, Comparator comparator) {
22 | if (list == null || list.isEmpty()) {
23 | return;
24 | }
25 | Collections.sort(list, comparator);
26 | }
27 |
28 | /**
29 | * Returns either an immutable copy of a list, or an empty list if the list is null.
30 | * @return An immutable copy of the list, guaranteed to be non-null.
31 | */
32 | public static List immutableOrEmpty(List list) {
33 | return list == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(list);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/keyframe/src/test/java/com/facebook/keyframes/KFPathTest.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes;
11 |
12 | import android.graphics.Matrix;
13 | import android.graphics.Path;
14 |
15 | import junit.framework.Assert;
16 |
17 | import org.junit.Test;
18 | import org.mockito.InOrder;
19 | import org.mockito.Mockito;
20 |
21 | public class KFPathTest {
22 |
23 | private class KFPathTestObject {
24 | final Path mockPath;
25 | final KFPath kfPath;
26 |
27 | public KFPathTestObject() {
28 | mockPath = Mockito.mock(Path.class);
29 | kfPath = new KFPath(mockPath, new float[]{0, 0});
30 | }
31 | }
32 |
33 | @Test
34 | public void testSimpleCommands() {
35 | KFPathTestObject kfPathWrapper = new KFPathTestObject();
36 | InOrder orderedMock = Mockito.inOrder(kfPathWrapper.mockPath);
37 |
38 | kfPathWrapper.kfPath.isEmpty();
39 | orderedMock.verify(kfPathWrapper.mockPath).isEmpty();
40 |
41 | Matrix matrix = new Matrix();
42 | kfPathWrapper.kfPath.transform(matrix);
43 | orderedMock.verify(kfPathWrapper.mockPath).transform(matrix);
44 | }
45 |
46 | @Test
47 | public void testDrawingCommandsAndLastPoint() {
48 | KFPathTestObject kfPathWrapper = new KFPathTestObject();
49 | InOrder orderedMock = Mockito.inOrder(kfPathWrapper.mockPath);
50 |
51 | kfPathWrapper.kfPath.moveTo(1.1f, 1.2f);
52 | orderedMock.verify(kfPathWrapper.mockPath).moveTo(1.1f, 1.2f);
53 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[0], 1.1f);
54 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[1], 1.2f);
55 |
56 | kfPathWrapper.kfPath.rMoveTo(2.1f, 2.2f);
57 | orderedMock.verify(kfPathWrapper.mockPath).rMoveTo(2.1f, 2.2f);
58 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[0], 1.1f + 2.1f);
59 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[1], 1.2f + 2.2f);
60 |
61 | kfPathWrapper.kfPath.reset();
62 | orderedMock.verify(kfPathWrapper.mockPath).reset();
63 |
64 | kfPathWrapper.kfPath.lineTo(1.1f, 1.2f);
65 | orderedMock.verify(kfPathWrapper.mockPath).lineTo(1.1f, 1.2f);
66 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[0], 1.1f);
67 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[1], 1.2f);
68 |
69 | kfPathWrapper.kfPath.rLineTo(2.1f, 2.2f);
70 | orderedMock.verify(kfPathWrapper.mockPath).rLineTo(2.1f, 2.2f);
71 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[0], 1.1f + 2.1f);
72 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[1], 1.2f + 2.2f);
73 |
74 | kfPathWrapper.kfPath.reset();
75 | orderedMock.verify(kfPathWrapper.mockPath).reset();
76 |
77 | kfPathWrapper.kfPath.quadTo(1.1f, 1.2f, 1.3f, 1.4f);
78 | orderedMock.verify(kfPathWrapper.mockPath).quadTo(1.1f, 1.2f, 1.3f, 1.4f);
79 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[0], 1.3f);
80 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[1], 1.4f);
81 |
82 | kfPathWrapper.kfPath.rQuadTo(2.1f, 2.2f, 2.3f, 2.4f);
83 | orderedMock.verify(kfPathWrapper.mockPath).rQuadTo(2.1f, 2.2f, 2.3f, 2.4f);
84 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[0], 1.3f + 2.3f);
85 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[1], 1.4f + 2.4f);
86 |
87 | kfPathWrapper.kfPath.reset();
88 | orderedMock.verify(kfPathWrapper.mockPath).reset();
89 |
90 | kfPathWrapper.kfPath.cubicTo(1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f);
91 | orderedMock.verify(kfPathWrapper.mockPath).cubicTo(1.1f, 1.2f, 1.3f, 1.4f, 1.5f, 1.6f);
92 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[0], 1.5f);
93 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[1], 1.6f);
94 |
95 | kfPathWrapper.kfPath.rCubicTo(2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f);
96 | orderedMock.verify(kfPathWrapper.mockPath).rCubicTo(2.1f, 2.2f, 2.3f, 2.4f, 2.5f, 2.6f);
97 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[0], 1.5f + 2.5f);
98 | Assert.assertEquals(kfPathWrapper.kfPath.getLastPoint()[1], 1.6f + 2.6f);
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/keyframe/src/test/java/com/facebook/keyframes/model/ModelTests.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2016-present, Facebook, Inc.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree. An additional grant
7 | * of patent rights can be found in the PATENTS file in the same directory.
8 | */
9 |
10 | package com.facebook.keyframes.model;
11 |
12 | import org.junit.Assert;
13 | import org.junit.Test;
14 | import org.mockito.Mockito;
15 |
16 | import java.util.ArrayList;
17 | import java.util.Arrays;
18 |
19 | public class ModelTests {
20 |
21 | @Test
22 | public void testMatrixOrderingAnimationGroup() {
23 | KFAnimationGroup.Builder builder = new KFAnimationGroup.Builder();
24 | builder.groupId = 1;
25 | builder.animations = Arrays.asList(
26 | createDummyAnimationOfType(KFAnimation.PropertyType.ROTATION),
27 | createDummyAnimationOfType(KFAnimation.PropertyType.X_POSITION),
28 | createDummyAnimationOfType(KFAnimation.PropertyType.SCALE));
29 | KFAnimationGroup group = builder.build();
30 |
31 | Assert.assertEquals(
32 | KFAnimation.PropertyType.SCALE,
33 | group.getAnimations().get(0).getPropertyType());
34 | Assert.assertEquals(
35 | KFAnimation.PropertyType.ROTATION,
36 | group.getAnimations().get(1).getPropertyType());
37 | Assert.assertEquals(
38 | KFAnimation.PropertyType.X_POSITION,
39 | group.getAnimations().get(2).getPropertyType());
40 | }
41 |
42 | @Test
43 | public void testFeatureAnimationOrderingAndExtraction() {
44 | // Test transform matrix ordering is correct, as well as filtering out stroke_width from matrix
45 | // based animations.
46 | KFFeature.Builder builder = new KFFeature.Builder();
47 | KFAnimation dummyStrokeWidth =
48 | createDummyAnimationOfType(KFAnimation.PropertyType.STROKE_WIDTH);
49 | builder.featureAnimations = new ArrayList<>(Arrays.asList(
50 | createDummyAnimationOfType(KFAnimation.PropertyType.ROTATION),
51 | dummyStrokeWidth,
52 | createDummyAnimationOfType(KFAnimation.PropertyType.X_POSITION),
53 | createDummyAnimationOfType(KFAnimation.PropertyType.SCALE)));
54 | KFFeature feature = builder.build();
55 |
56 | Assert.assertEquals(3, feature.mFeatureMatrixAnimations.size(), 0);
57 | Assert.assertEquals(
58 | KFAnimation.PropertyType.SCALE,
59 | feature.mFeatureMatrixAnimations.get(0).getPropertyType());
60 | Assert.assertEquals(
61 | KFAnimation.PropertyType.ROTATION,
62 | feature.mFeatureMatrixAnimations.get(1).getPropertyType());
63 | Assert.assertEquals(
64 | KFAnimation.PropertyType.X_POSITION,
65 | feature.mFeatureMatrixAnimations.get(2).getPropertyType());
66 | Assert.assertEquals(
67 | dummyStrokeWidth,
68 | feature.mStrokeWidthAnimation);
69 | }
70 |
71 | private KFAnimation createDummyAnimationOfType(KFAnimation.PropertyType type) {
72 | KFAnimation animation = Mockito.mock(KFAnimation.class);
73 | Mockito.when(animation.getPropertyType()).thenReturn(type);
74 | return animation;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', 'keyframe'
2 |
--------------------------------------------------------------------------------