1 /*
2  * Copyright (C) 2007-2008 Esmertec AG.
3  * Copyright (C) 2007-2008 The Android Open Source Project
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.mms.dom.smil;
19 
20 import com.android.mms.LogTag;
21 
22 import java.util.ArrayList;
23 
24 import org.w3c.dom.DOMException;
25 import org.w3c.dom.smil.ElementTime;
26 import org.w3c.dom.smil.SMILElement;
27 import org.w3c.dom.smil.Time;
28 import org.w3c.dom.smil.TimeList;
29 
30 import android.util.Log;
31 
32 public abstract class ElementTimeImpl implements ElementTime {
33     private static final String TAG = LogTag.TAG;
34 
35     private static final String FILL_REMOVE_ATTRIBUTE = "remove";
36     private static final String FILL_FREEZE_ATTRIBUTE = "freeze";
37     private static final String FILL_HOLD_ATTRIBUTE = "hold";
38     private static final String FILL_TRANSITION_ATTRIBUTE = "transition";
39     private static final String FILL_AUTO_ATTRIBUTE   = "auto";
40     private static final String FILL_ATTRIBUTE_NAME   = "fill";
41     private static final String FILLDEFAULT_ATTRIBUTE_NAME   = "fillDefault";
42 
43     final SMILElement mSmilElement;
44 
45     /*
46      * Internal Interface
47      */
ElementTimeImpl(SMILElement element)48     ElementTimeImpl(SMILElement element) {
49         mSmilElement = element;
50     }
51 
52     // Default implementation. Override if required.
getBeginConstraints()53     int getBeginConstraints() {
54         return TimeImpl.ALLOW_ALL;
55     }
56 
57     // Default implementation. Override if required
getEndConstraints()58     int getEndConstraints() {
59         return TimeImpl.ALLOW_ALL;
60     }
61 
62     /**
63      * To get the parent node on the ElementTime tree. It is in opposition to getTimeChildren.
64      * @return the parent ElementTime. Returns <code>null</code> if there is no parent.
65      */
getParentElementTime()66     abstract ElementTime getParentElementTime();
67 
68     /*
69      * ElementTime Interface
70      */
71 
getBegin()72     public TimeList getBegin() {
73         String[] beginTimeStringList = mSmilElement.getAttribute("begin").split(";");
74 
75         // TODO: Check other constraints on parsed values, e.g., "single, non-negative offset values
76         ArrayList<Time> beginTimeList = new ArrayList<Time>();
77         // Initialize Time instances and add them to Vector
78         for (int i = 0; i < beginTimeStringList.length; i++) {
79             try {
80                 beginTimeList.add(new TimeImpl(beginTimeStringList[i], getBeginConstraints()));
81             } catch (IllegalArgumentException e) {
82                 // Ignore badly formatted times
83             }
84         }
85         if (beginTimeList.size() == 0) {
86             /*
87              * What is the right default value?
88              *
89              * In MMS SMIL, this method may be called either on an instance of:
90              *
91              * 1 - ElementSequentialTimeContainer (The SMILDocument)
92              * 2 - ElementParallelTimeContainer (A Time-Child of the SMILDocument, which is a seq)
93              * 3 - ElementTime (A SMILMediaElement).
94              *
95              * 1 - In the first case, the default start time is obviously 0.
96              * 2 - In the second case, the specifications mentions that
97              *      "For children of a sequence, the only legal value for begin is
98              *      a (single) non-negative offset value. The default begin value is 0."
99              * 3 - In the third case, the specification mentions that
100              *      "The default value of begin for children of a par is 0."
101              *
102              * In short, if no value is specified, the default is always 0.
103              */
104 
105             beginTimeList.add(new TimeImpl("0", TimeImpl.ALLOW_ALL));
106         }
107         return new TimeListImpl(beginTimeList);
108     }
109 
getDur()110     public float getDur() {
111         float dur = 0;
112         try {
113             String durString = mSmilElement.getAttribute("dur");
114             if (durString != null) {
115                 dur = TimeImpl.parseClockValue(durString) / 1000f;
116             }
117         } catch (IllegalArgumentException e) {
118             // Do nothing and return the minimum value
119         }
120 
121         return dur;
122     }
123 
getEnd()124     public TimeList getEnd() {
125         ArrayList<Time> endTimeList = new ArrayList<Time>();
126 
127         String[] endTimeStringList = mSmilElement.getAttribute("end").split(";");
128         int len = endTimeStringList.length;
129         if (!((len == 1) && (endTimeStringList[0].length() == 0))) {  // Ensure the end field is set.
130             // Initialize Time instances and add them to Vector
131             for (int i = 0; i < len; i++) {
132                 try {
133                     endTimeList.add(new TimeImpl(endTimeStringList[i],
134                             getEndConstraints()));
135                 } catch (IllegalArgumentException e) {
136                     // Ignore badly formatted times
137                     Log.e(TAG, "Malformed time value.", e);
138                 }
139             }
140         }
141 
142         // "end" time is not specified
143         if (endTimeList.size() == 0) {
144             // Get duration
145             float duration = getDur();
146 
147             if (duration < 0) {
148                 endTimeList.add(new TimeImpl("indefinite", getEndConstraints()));
149             } else {
150                 // Get begin
151                 TimeList begin = getBegin();
152                 for (int i = 0; i < begin.getLength(); i++) {
153                     endTimeList.add(new TimeImpl(
154                             // end = begin + dur
155                             begin.item(i).getResolvedOffset() + duration + "s",
156                             getEndConstraints()));
157                 }
158             }
159         }
160 
161         return new TimeListImpl(endTimeList);
162     }
163 
beginAndEndAreZero()164     private boolean beginAndEndAreZero() {
165         TimeList begin = getBegin();
166         TimeList end = getEnd();
167         if (begin.getLength() == 1 && end.getLength() == 1) {
168             Time beginTime = begin.item(0);
169             Time endTime = end.item(0);
170             return beginTime.getOffset() == 0. && endTime.getOffset() == 0.;
171         }
172         return false;
173     }
174 
getFill()175     public short getFill() {
176         String fill = mSmilElement.getAttribute(FILL_ATTRIBUTE_NAME);
177         if (fill.equalsIgnoreCase(FILL_FREEZE_ATTRIBUTE)) {
178             return FILL_FREEZE;
179         } else if (fill.equalsIgnoreCase(FILL_REMOVE_ATTRIBUTE)) {
180             return FILL_REMOVE;
181         } else if (fill.equalsIgnoreCase(FILL_HOLD_ATTRIBUTE)) {
182             // FIXME handle it as freeze for now
183             return FILL_FREEZE;
184         } else if (fill.equalsIgnoreCase(FILL_TRANSITION_ATTRIBUTE)) {
185             // FIXME handle it as freeze for now
186             return FILL_FREEZE;
187         } else if (!fill.equalsIgnoreCase(FILL_AUTO_ATTRIBUTE)) {
188             /*
189              * fill = default
190              * The fill behavior for the element is determined by the value of the fillDefault
191              * attribute.  This is the default value.
192              */
193             short fillDefault = getFillDefault();
194             if (fillDefault != FILL_AUTO) {
195                 return fillDefault;
196             }
197         }
198 
199         /*
200          * fill = auto
201          * The fill behavior for this element depends on whether the element specifies any of
202          * the attributes that define the simple or active duration:
203          *  - If none of the attributes dur, end, repeatCount or repeatDur are specified on
204          *    the element, then the element will have a fill behavior identical to that if it were
205          *    specified as "freeze".
206          *  - Otherwise, the element will have a fill behavior identical to that if it were
207          *    specified as "remove".
208          */
209         if (((mSmilElement.getAttribute("dur").length() == 0) &&
210                 (mSmilElement.getAttribute("end").length() == 0) &&
211                 (mSmilElement.getAttribute("repeatCount").length() == 0) &&
212                 (mSmilElement.getAttribute("repeatDur").length() == 0)) ||
213                 beginAndEndAreZero()) {
214             return FILL_FREEZE;
215         } else {
216             return FILL_REMOVE;
217         }
218     }
219 
getFillDefault()220     public short getFillDefault() {
221         String fillDefault = mSmilElement.getAttribute(FILLDEFAULT_ATTRIBUTE_NAME);
222         if (fillDefault.equalsIgnoreCase(FILL_REMOVE_ATTRIBUTE)) {
223             return FILL_REMOVE;
224         } else if (fillDefault.equalsIgnoreCase(FILL_FREEZE_ATTRIBUTE)) {
225             return FILL_FREEZE;
226         } else if (fillDefault.equalsIgnoreCase(FILL_AUTO_ATTRIBUTE)) {
227             return FILL_AUTO;
228         } else if (fillDefault.equalsIgnoreCase(FILL_HOLD_ATTRIBUTE)) {
229             // FIXME handle it as freeze for now
230             return FILL_FREEZE;
231         } else if (fillDefault.equalsIgnoreCase(FILL_TRANSITION_ATTRIBUTE)) {
232             // FIXME handle it as freeze for now
233             return FILL_FREEZE;
234         } else {
235             /*
236              * fillDefault = inherit
237              * Specifies that the value of this attribute (and of the fill behavior) are
238              * inherited from the fillDefault value of the parent element.
239              * This is the default value.
240              */
241             ElementTime parent = getParentElementTime();
242             if (parent == null) {
243                 /*
244                  * fillDefault = auto
245                  * If there is no parent element, the value is "auto".
246                  */
247                 return FILL_AUTO;
248             } else {
249                 return ((ElementTimeImpl) parent).getFillDefault();
250             }
251         }
252     }
253 
getRepeatCount()254     public float getRepeatCount() {
255         String repeatCount = mSmilElement.getAttribute("repeatCount");
256         try {
257             float value = Float.parseFloat(repeatCount);
258             if (value > 0) {
259                 return value;
260             } else {
261                 return 0; // default
262             }
263         } catch (NumberFormatException e) {
264             return 0; // default
265         }
266     }
267 
getRepeatDur()268     public float getRepeatDur() {
269         try {
270             float repeatDur =
271                 TimeImpl.parseClockValue(mSmilElement.getAttribute("repeatDur"));
272             if (repeatDur > 0) {
273                 return repeatDur;
274             } else {
275                 return 0; // default
276             }
277         } catch (IllegalArgumentException e) {
278             return 0; // default
279         }
280     }
281 
getRestart()282     public short getRestart() {
283         String restart = mSmilElement.getAttribute("restart");
284         if (restart.equalsIgnoreCase("never")) {
285             return RESTART_NEVER;
286         } else if (restart.equalsIgnoreCase("whenNotActive")) {
287             return RESTART_WHEN_NOT_ACTIVE;
288         } else {
289             return RESTART_ALWAYS; // default
290         }
291     }
292 
setBegin(TimeList begin)293     public void setBegin(TimeList begin) throws DOMException {
294         // TODO Implement this
295         mSmilElement.setAttribute("begin", "indefinite");
296     }
297 
setDur(float dur)298     public void setDur(float dur) throws DOMException {
299         // In SMIL 3.0, the dur could be a timecount-value which may contain fractions.
300         // However, in MMS 1.3, the dur SHALL be expressed in integer milliseconds.
301         mSmilElement.setAttribute("dur", Integer.toString((int)(dur * 1000)) + "ms");
302     }
303 
setEnd(TimeList end)304     public void setEnd(TimeList end) throws DOMException {
305         // TODO Implement this
306         mSmilElement.setAttribute("end", "indefinite");
307     }
308 
setFill(short fill)309     public void setFill(short fill) throws DOMException {
310         if (fill == FILL_FREEZE) {
311             mSmilElement.setAttribute(FILL_ATTRIBUTE_NAME, FILL_FREEZE_ATTRIBUTE);
312         } else {
313             mSmilElement.setAttribute(FILL_ATTRIBUTE_NAME, FILL_REMOVE_ATTRIBUTE); // default
314         }
315     }
316 
setFillDefault(short fillDefault)317     public void setFillDefault(short fillDefault) throws DOMException {
318         if (fillDefault == FILL_FREEZE) {
319             mSmilElement.setAttribute(FILLDEFAULT_ATTRIBUTE_NAME, FILL_FREEZE_ATTRIBUTE);
320         } else {
321             mSmilElement.setAttribute(FILLDEFAULT_ATTRIBUTE_NAME, FILL_REMOVE_ATTRIBUTE);
322         }
323     }
324 
setRepeatCount(float repeatCount)325     public void setRepeatCount(float repeatCount) throws DOMException {
326         String repeatCountString = "indefinite";
327         if (repeatCount > 0) {
328             repeatCountString = Float.toString(repeatCount);
329         }
330         mSmilElement.setAttribute("repeatCount", repeatCountString);
331     }
332 
setRepeatDur(float repeatDur)333     public void setRepeatDur(float repeatDur) throws DOMException {
334         String repeatDurString = "indefinite";
335         if (repeatDur > 0) {
336             repeatDurString = Float.toString(repeatDur) + "ms";
337         }
338         mSmilElement.setAttribute("repeatDur", repeatDurString);
339     }
340 
setRestart(short restart)341     public void setRestart(short restart) throws DOMException {
342         if (restart == RESTART_NEVER) {
343             mSmilElement.setAttribute("restart", "never");
344         } else if (restart == RESTART_WHEN_NOT_ACTIVE) {
345             mSmilElement.setAttribute("restart", "whenNotActive");
346         } else {
347             mSmilElement.setAttribute("restart", "always");
348         }
349     }
350 }
351