1 /*
2  * Copyright (C) 2018 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 androidx.constraintlayout.motion.widget;
18 
19 import android.content.Context;
20 import android.content.res.XmlResourceParser;
21 import android.util.Log;
22 import android.util.Xml;
23 
24 import androidx.constraintlayout.widget.ConstraintAttribute;
25 import androidx.constraintlayout.widget.ConstraintLayout;
26 
27 import org.xmlpull.v1.XmlPullParser;
28 import org.xmlpull.v1.XmlPullParserException;
29 
30 import java.io.IOException;
31 import java.lang.reflect.Constructor;
32 import java.util.ArrayList;
33 import java.util.HashMap;
34 import java.util.Set;
35 
36 /**
37  * The parses the KeyFrame structure in a MotionScene xml
38  *
39  */
40 
41 public class KeyFrames {
42     public static final int UNSET = ConstraintLayout.LayoutParams.UNSET;
43     private static final String CUSTOM_METHOD = "CustomMethod";
44     private static final String CUSTOM_ATTRIBUTE = "CustomAttribute";
45     private HashMap<Integer, ArrayList<Key>> mFramesMap = new HashMap<Integer, ArrayList<Key>>();
46     static HashMap<String, Constructor<? extends Key>> sKeyMakers = new HashMap<>();
47     private static final String TAG = "KeyFrames";
48 
49     static {
50         try {
sKeyMakers.put(KeyAttributes.NAME, KeyAttributes.class.getConstructor())51             sKeyMakers.put(KeyAttributes.NAME, KeyAttributes.class.getConstructor());
sKeyMakers.put(KeyPosition.NAME, KeyPosition.class.getConstructor())52             sKeyMakers.put(KeyPosition.NAME, KeyPosition.class.getConstructor());
sKeyMakers.put(KeyCycle.NAME, KeyCycle.class.getConstructor())53             sKeyMakers.put(KeyCycle.NAME, KeyCycle.class.getConstructor());
sKeyMakers.put(KeyTimeCycle.NAME, KeyTimeCycle.class.getConstructor())54             sKeyMakers.put(KeyTimeCycle.NAME, KeyTimeCycle.class.getConstructor());
sKeyMakers.put(KeyTrigger.NAME, KeyTrigger.class.getConstructor())55             sKeyMakers.put(KeyTrigger.NAME, KeyTrigger.class.getConstructor());
56 
57         } catch (NoSuchMethodException e) {
58             Log.e(TAG, "unable to load", e);
59         }
60     }
61 
62     /**
63      * Add a key to this set of keyframes
64      * @param key
65      */
addKey(Key key)66     public void addKey(Key key) {
67         if (!mFramesMap.containsKey(key.mTargetId)) {
68             mFramesMap.put(key.mTargetId, new ArrayList<>());
69         }
70         ArrayList<Key> frames = mFramesMap.get(key.mTargetId);
71         if (frames != null) {
72             frames.add(key);
73         }
74     }
75 
KeyFrames()76     public KeyFrames() {
77 
78     }
79 
KeyFrames(Context context, XmlPullParser parser)80     public KeyFrames(Context context, XmlPullParser parser) {
81         String tagName = null;
82         try {
83             Key key = null;
84             for (int eventType = parser.getEventType();
85                     eventType != XmlResourceParser.END_DOCUMENT;
86                     eventType = parser.next()) {
87                 switch (eventType) {
88                     case XmlResourceParser.START_DOCUMENT:
89                         break;
90                     case XmlResourceParser.START_TAG:
91                         tagName = parser.getName();
92 
93                         if (sKeyMakers.containsKey(tagName)) {
94 
95                             switch (tagName) {
96                                 case KeyAttributes.NAME:
97                                     key = new KeyAttributes();
98                                     break;
99                                 case KeyPosition.NAME:
100                                     key = new KeyPosition();
101                                     break;
102                                 case KeyCycle.NAME:
103                                     key = new KeyCycle();
104                                     break;
105                                 case KeyTimeCycle.NAME:
106                                     key = new KeyTimeCycle();
107                                     break;
108                                 case KeyTrigger.NAME:
109                                     key = new KeyTrigger();
110                                     break;
111                                 default:
112                                     throw new NullPointerException(
113                                             "Key " + tagName + " not found");
114                             }
115                             key.load(context, Xml.asAttributeSet(parser));
116                             addKey(key);
117 
118                         } else if (tagName.equalsIgnoreCase(CUSTOM_ATTRIBUTE)) {
119                             if (key != null && key.mCustomConstraints != null) {
120                                 ConstraintAttribute.parse(context, parser, key.mCustomConstraints);
121                             }
122                         } else if (tagName.equalsIgnoreCase(CUSTOM_METHOD)) {
123                             if (key != null && key.mCustomConstraints != null) {
124                                 ConstraintAttribute.parse(context, parser, key.mCustomConstraints);
125                             }
126                         }
127                         break;
128                     case XmlResourceParser.END_TAG:
129                         if ("KeyFrameSet".equals(parser.getName())) {
130                             return;
131                         }
132                         break;
133                     case XmlResourceParser.TEXT:
134                         break;
135                 }
136             }
137         } catch (XmlPullParserException e) {
138             Log.e(TAG, "Error parsing XML resource", e);
139         } catch (IOException e) {
140             Log.e(TAG, "Error parsing XML resource", e);
141         }
142     }
143 
144     /**
145      * Do not filter the set by matches
146      * @param motionController
147      */
addAllFrames(MotionController motionController)148     public void addAllFrames(MotionController motionController) {
149         ArrayList<Key> list = mFramesMap.get(UNSET);
150         if (list != null) {
151             motionController.addKeys(list);
152         }
153     }
154 
155     /**
156      * add the key frames to the motion controller
157      * @param motionController
158      */
addFrames(MotionController motionController)159     public void addFrames(MotionController motionController) {
160         ArrayList<Key> list = mFramesMap.get(motionController.mId);
161         if (list != null) {
162             motionController.addKeys(list);
163         }
164         list = mFramesMap.get(UNSET);
165 
166         if (list != null) {
167             for (Key key : list) {
168                 String tag =
169                         ((ConstraintLayout.LayoutParams)
170                                 motionController.mView.getLayoutParams()).constraintTag;
171                 if (key.matches(tag)) {
172                     motionController.addKey(key);
173                 }
174             }
175 
176         }
177 
178     }
179 
name(int viewId, Context context)180     static String name(int viewId, Context context) {
181         return context.getResources().getResourceEntryName(viewId);
182     }
183 
getKeys()184     public Set<Integer> getKeys() {
185         return mFramesMap.keySet();
186     }
187 
188     /**
189      * Get the list of keyframes given and ID
190      * @param id
191      * @return
192      */
getKeyFramesForView(int id)193     public ArrayList<Key> getKeyFramesForView(int id) {
194         return mFramesMap.get(id);
195     }
196 }
197