1 /* 2 * Copyright (C) 2008 Esmertec AG. 3 * Copyright (C) 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.model; 19 20 import com.android.mms.UnsupportContentTypeException; 21 import com.android.mms.LogTag; 22 import com.android.mms.MmsConfig; 23 import android.drm.mobile1.DrmException; 24 import com.android.mms.drm.DrmWrapper; 25 import com.google.android.mms.ContentType; 26 import com.google.android.mms.MmsException; 27 import com.google.android.mms.pdu.CharacterSets; 28 import com.google.android.mms.pdu.PduBody; 29 import com.google.android.mms.pdu.PduPart; 30 31 import org.w3c.dom.smil.SMILMediaElement; 32 import org.w3c.dom.smil.SMILRegionElement; 33 import org.w3c.dom.smil.SMILRegionMediaElement; 34 import org.w3c.dom.smil.Time; 35 import org.w3c.dom.smil.TimeList; 36 37 import android.content.Context; 38 import android.util.Log; 39 40 import java.io.IOException; 41 42 public class MediaModelFactory { 43 private static final String TAG = "Mms:media"; 44 45 public static MediaModel getMediaModel(Context context, 46 SMILMediaElement sme, LayoutModel layouts, PduBody pb) 47 throws DrmException, IOException, IllegalArgumentException, MmsException { 48 String tag = sme.getTagName(); 49 String src = sme.getSrc(); 50 PduPart part = findPart(pb, src); 51 52 if (sme instanceof SMILRegionMediaElement) { 53 return getRegionMediaModel( 54 context, tag, src, (SMILRegionMediaElement) sme, layouts, part); 55 } else { 56 return getGenericMediaModel( 57 context, tag, src, sme, part, null); 58 } 59 } 60 61 private static PduPart findPart(PduBody pb, String src) { 62 PduPart part = null; 63 64 if (src != null) { 65 src = unescapeXML(src); 66 if (src.startsWith("cid:")) { 67 part = pb.getPartByContentId("<" + src.substring("cid:".length()) + ">"); 68 } else { 69 part = pb.getPartByName(src); 70 if (part == null) { 71 part = pb.getPartByFileName(src); 72 if (part == null) { 73 part = pb.getPartByContentLocation(src); 74 } 75 } 76 } 77 } 78 79 if (part != null) { 80 return part; 81 } 82 83 throw new IllegalArgumentException("No part found for the model."); 84 } 85 86 private static String unescapeXML(String str) { 87 return str.replaceAll("<","<") 88 .replaceAll(">", ">") 89 .replaceAll(""","\"") 90 .replaceAll("'","'") 91 .replaceAll("&", "&"); 92 } 93 94 private static MediaModel getRegionMediaModel(Context context, 95 String tag, String src, SMILRegionMediaElement srme, 96 LayoutModel layouts, PduPart part) throws DrmException, IOException, MmsException { 97 SMILRegionElement sre = srme.getRegion(); 98 if (sre != null) { 99 RegionModel region = layouts.findRegionById(sre.getId()); 100 if (region != null) { 101 return getGenericMediaModel(context, tag, src, srme, part, region); 102 } 103 } else { 104 String rId = null; 105 106 if (tag.equals(SmilHelper.ELEMENT_TAG_TEXT)) { 107 rId = LayoutModel.TEXT_REGION_ID; 108 } else { 109 rId = LayoutModel.IMAGE_REGION_ID; 110 } 111 112 RegionModel region = layouts.findRegionById(rId); 113 if (region != null) { 114 return getGenericMediaModel(context, tag, src, srme, part, region); 115 } 116 } 117 118 throw new IllegalArgumentException("Region not found or bad region ID."); 119 } 120 121 // When we encounter a content type we can't handle, such as "application/vnd.smaf", instead 122 // of throwing an exception and crashing, insert an empty TextModel in its place. 123 private static MediaModel createEmptyTextModel(Context context, DrmWrapper wrapper, 124 RegionModel regionModel) throws IOException { 125 return wrapper != null ? 126 new TextModel(context, ContentType.TEXT_PLAIN, null, CharacterSets.ANY_CHARSET, 127 wrapper, regionModel) : 128 new TextModel(context, ContentType.TEXT_PLAIN, null, regionModel); 129 } 130 131 private static MediaModel getGenericMediaModel(Context context, 132 String tag, String src, SMILMediaElement sme, PduPart part, 133 RegionModel regionModel) throws DrmException, IOException, MmsException { 134 byte[] bytes = part.getContentType(); 135 if (bytes == null) { 136 throw new IllegalArgumentException( 137 "Content-Type of the part may not be null."); 138 } 139 140 String contentType = new String(bytes); 141 MediaModel media = null; 142 if (ContentType.isDrmType(contentType)) { 143 DrmWrapper wrapper = new DrmWrapper( 144 contentType, part.getDataUri(), part.getData()); 145 if (tag.equals(SmilHelper.ELEMENT_TAG_TEXT)) { 146 media = new TextModel(context, contentType, src, 147 part.getCharset(), wrapper, regionModel); 148 } else if (tag.equals(SmilHelper.ELEMENT_TAG_IMAGE)) { 149 media = new ImageModel(context, contentType, src, 150 wrapper, regionModel); 151 } else if (tag.equals(SmilHelper.ELEMENT_TAG_VIDEO)) { 152 media = new VideoModel(context, contentType, src, 153 wrapper, regionModel); 154 } else if (tag.equals(SmilHelper.ELEMENT_TAG_AUDIO)) { 155 media = new AudioModel(context, contentType, src, 156 wrapper); 157 } else if (tag.equals(SmilHelper.ELEMENT_TAG_REF)) { 158 String drmContentType = wrapper.getContentType(); 159 if (ContentType.isTextType(drmContentType)) { 160 media = new TextModel(context, contentType, src, 161 part.getCharset(), wrapper, regionModel); 162 } else if (ContentType.isImageType(drmContentType)) { 163 media = new ImageModel(context, contentType, src, 164 wrapper, regionModel); 165 } else if (ContentType.isVideoType(drmContentType)) { 166 media = new VideoModel(context, contentType, src, 167 wrapper, regionModel); 168 } else if (ContentType.isAudioType(drmContentType)) { 169 media = new AudioModel(context, contentType, src, 170 wrapper); 171 } else { 172 Log.d(TAG, "[MediaModelFactory] getGenericMediaModel Unsupported Content-Type: " 173 + contentType); 174 media = createEmptyTextModel(context, wrapper, regionModel); 175 } 176 } else { 177 throw new IllegalArgumentException("Unsupported TAG: " + tag); 178 } 179 } else { 180 if (tag.equals(SmilHelper.ELEMENT_TAG_TEXT)) { 181 media = new TextModel(context, contentType, src, 182 part.getCharset(), part.getData(), regionModel); 183 } else if (tag.equals(SmilHelper.ELEMENT_TAG_IMAGE)) { 184 media = new ImageModel(context, contentType, src, 185 part.getDataUri(), regionModel); 186 } else if (tag.equals(SmilHelper.ELEMENT_TAG_VIDEO)) { 187 media = new VideoModel(context, contentType, src, 188 part.getDataUri(), regionModel); 189 } else if (tag.equals(SmilHelper.ELEMENT_TAG_AUDIO)) { 190 media = new AudioModel(context, contentType, src, 191 part.getDataUri()); 192 } else if (tag.equals(SmilHelper.ELEMENT_TAG_REF)) { 193 if (ContentType.isTextType(contentType)) { 194 media = new TextModel(context, contentType, src, 195 part.getCharset(), part.getData(), regionModel); 196 } else if (ContentType.isImageType(contentType)) { 197 media = new ImageModel(context, contentType, src, 198 part.getDataUri(), regionModel); 199 } else if (ContentType.isVideoType(contentType)) { 200 media = new VideoModel(context, contentType, src, 201 part.getDataUri(), regionModel); 202 } else if (ContentType.isAudioType(contentType)) { 203 media = new AudioModel(context, contentType, src, 204 part.getDataUri()); 205 } else { 206 Log.d(TAG, "[MediaModelFactory] getGenericMediaModel Unsupported Content-Type: " 207 + contentType); 208 media = createEmptyTextModel(context, null, regionModel); 209 } 210 } else { 211 throw new IllegalArgumentException("Unsupported TAG: " + tag); 212 } 213 } 214 215 // Set 'begin' property. 216 int begin = 0; 217 TimeList tl = sme.getBegin(); 218 if ((tl != null) && (tl.getLength() > 0)) { 219 // We only support a single begin value. 220 Time t = tl.item(0); 221 begin = (int) (t.getResolvedOffset() * 1000); 222 } 223 media.setBegin(begin); 224 225 // Set 'duration' property. 226 int duration = (int) (sme.getDur() * 1000); 227 if (duration <= 0) { 228 tl = sme.getEnd(); 229 if ((tl != null) && (tl.getLength() > 0)) { 230 // We only support a single end value. 231 Time t = tl.item(0); 232 if (t.getTimeType() != Time.SMIL_TIME_INDEFINITE) { 233 duration = (int) (t.getResolvedOffset() * 1000) - begin; 234 235 if (duration == 0 && 236 (media instanceof AudioModel || media instanceof VideoModel)) { 237 duration = MmsConfig.getMinimumSlideElementDuration(); 238 if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) { 239 Log.d(TAG, "[MediaModelFactory] compute new duration for " + tag + 240 ", duration=" + duration); 241 } 242 } 243 } 244 } 245 } 246 247 media.setDuration(duration); 248 249 if (!MmsConfig.getSlideDurationEnabled()) { 250 /** 251 * Because The slide duration is not supported by mmsc, 252 * the device has to set fill type as FILL_FREEZE. 253 * If not, the media will disappear while rotating the screen 254 * in the slide show play view. 255 */ 256 media.setFill(sme.FILL_FREEZE); 257 } else { 258 // Set 'fill' property. 259 media.setFill(sme.getFill()); 260 } 261 return media; 262 } 263 } 264