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