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