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