1 /* 2 * Copyright 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.sampletvinput.rich; 18 19 import com.example.android.sampletvinput.rich.RichTvInputService.ChannelInfo; 20 import com.example.android.sampletvinput.rich.RichTvInputService.ProgramInfo; 21 import com.example.android.sampletvinput.rich.RichTvInputService.TvInput; 22 import com.example.android.sampletvinput.TvContractUtils; 23 import com.example.android.sampletvinput.player.TvInputPlayer; 24 25 import org.xmlpull.v1.XmlPullParser; 26 import org.xmlpull.v1.XmlPullParserException; 27 28 import java.io.IOException; 29 import java.text.SimpleDateFormat; 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** 34 * A parser for the channels and programs feed used in {@link RichTvInputService}. 35 * <p> 36 * NOTE: The feed format here is just an example. Developers may invent any new formats and parse 37 * them in their own way. 38 * </p> 39 */ 40 public class ChannelXMLParser { 41 private static String TAG = "ChannelXmlParser"; 42 43 private static final String TAG_TVINPUTS = "TvInputs"; 44 private static final String TAG_CHANNELS = "Channels"; 45 private static final String TAG_CHANNEL = "Channel"; 46 private static final String TAG_PROGRAM = "Program"; 47 48 private static final String ATTR_TVINPUT_DISPLAY_NAME = "display_name"; 49 private static final String ATTR_TVINPUT_NAME = "name"; 50 private static final String ATTR_TVINPUT_DESCRIPTION = "description"; 51 private static final String ATTR_LOGO_THUMB_URL = "logo_thumb_url"; 52 private static final String ATTR_LOGO_BACKGROUND_URL = "logo_background_url"; 53 54 private static final String ATTR_DISPLAY_NUMBER = "display_number"; 55 private static final String ATTR_DISPLAY_NAME = "display_name"; 56 private static final String ATTR_VIDEO_WIDTH = "video_width"; 57 private static final String ATTR_VIDEO_HEIGHT = "video_height"; 58 private static final String ATTR_LOGO_URL = "logo_url"; 59 60 private static final String ATTR_TITLE = "title"; 61 private static final String ATTR_POSTER_ART_URL = "poster_art_url"; 62 private static final String ATTR_DURATION_SEC = "duration_sec"; 63 private static final String ATTR_VIDEO_URL = "video_url"; 64 private static final String ATTR_VIDEO_TYPE = "video_type"; 65 private static final String ATTR_DESCRIPTION = "description"; 66 private static final String ATTR_CONTENT_RATING = "content_rating"; 67 68 private static final String VALUE_VIDEO_TYPE_HTTP_PROGRESSIVE = "HTTP_PROGRESSIVE"; 69 private static final String VALUE_VIDEO_TYPE_HLS = "HLS"; 70 private static final String VALUE_VIDEO_TYPE_MPEG_DASH = "MPEG_DASH"; 71 72 public static TvInput parseTvInput(XmlPullParser parser) 73 throws XmlPullParserException, IOException { 74 parser.nextTag(); 75 parser.require(XmlPullParser.START_TAG, null, TAG_TVINPUTS); 76 return parseTvInputDetail(parser); 77 } 78 79 private static TvInput parseTvInputDetail(XmlPullParser parser) { 80 String displayName = null; 81 String name = null; 82 String description = null; 83 String logoThumbUrl = null; 84 String logoBackgroundUrl = null; 85 for (int i = 0; i < parser.getAttributeCount(); ++i) { 86 String attr = parser.getAttributeName(i); 87 String value = parser.getAttributeValue(i); 88 if (ATTR_TVINPUT_DISPLAY_NAME.equals(attr)) { 89 displayName = value; 90 } else if (ATTR_TVINPUT_NAME.equals(attr)) { 91 name = value; 92 } else if (ATTR_TVINPUT_DESCRIPTION.equals(attr)) { 93 description = value; 94 } else if (ATTR_LOGO_THUMB_URL.equals(attr)) { 95 logoThumbUrl = value; 96 } else if (ATTR_LOGO_BACKGROUND_URL.equals(attr)) { 97 logoBackgroundUrl = value; 98 } 99 } 100 return new TvInput(displayName, name, description, logoThumbUrl, logoBackgroundUrl); 101 } 102 103 public static List<ChannelInfo> parseChannelXML(XmlPullParser parser) 104 throws XmlPullParserException, IOException { 105 List<ChannelInfo> list = new ArrayList<ChannelInfo>(); 106 parser.nextTag(); 107 108 parser.require(XmlPullParser.START_TAG, null, TAG_CHANNELS); 109 while (parser.next() != XmlPullParser.END_DOCUMENT) { 110 if (parser.getEventType() == XmlPullParser.START_TAG 111 && TAG_CHANNEL.equals(parser.getName())) { 112 list.add(parseChannel(parser)); 113 } 114 } 115 return list; 116 } 117 118 private static ChannelInfo parseChannel(XmlPullParser parser) 119 throws XmlPullParserException, IOException { 120 String displayNumber = null; 121 String displayName = null; 122 int videoWidth = 0; 123 int videoHeight = 0; 124 String logoUrl = null; 125 StringBuilder hashString = new StringBuilder(); 126 for (int i = 0; i < parser.getAttributeCount(); ++i) { 127 String attr = parser.getAttributeName(i); 128 String value = parser.getAttributeValue(i); 129 // Here we assume that other metadata except number and name may change when the feed 130 // is updated. 131 if (ATTR_DISPLAY_NUMBER.equals(attr)) { 132 displayNumber = value; 133 hashString.append(value).append(";"); 134 } else if (ATTR_DISPLAY_NAME.equals(attr)) { 135 displayName = value; 136 hashString.append(value).append(";"); 137 } else if (ATTR_VIDEO_WIDTH.equals(attr)) { 138 videoWidth = Integer.parseInt(value); 139 } else if (ATTR_VIDEO_HEIGHT.equals(attr)) { 140 videoHeight = Integer.parseInt(value); 141 } else if (ATTR_LOGO_URL.equals(attr)) { 142 logoUrl = value; 143 } 144 } 145 List<ProgramInfo> programs = new ArrayList<>(); 146 while (parser.next() != XmlPullParser.END_DOCUMENT) { 147 if (parser.getEventType() == XmlPullParser.START_TAG) { 148 if (TAG_PROGRAM.equals(parser.getName())) { 149 programs.add(parseProgram(parser)); 150 } 151 } else if (TAG_CHANNEL.equals(parser.getName()) 152 && parser.getEventType() == XmlPullParser.END_TAG) { 153 break; 154 } 155 } 156 // Developers should assign original network ID in the right way not using the fake ID. 157 int fakeOriginalNetworkId = hashString.toString().hashCode(); 158 return new ChannelInfo(displayNumber, displayName, logoUrl, fakeOriginalNetworkId, 0, 0, 159 videoWidth, videoHeight, programs); 160 } 161 162 private static ProgramInfo parseProgram(XmlPullParser parser) { 163 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm"); 164 String title = null; 165 long durationSec = 0; 166 String videoUrl = null; 167 int videoType = TvInputPlayer.SOURCE_TYPE_HTTP_PROGRESSIVE; 168 String description = null; 169 String posterArtUri = null; 170 String contentRatings = null; 171 for (int i = 0; i < parser.getAttributeCount(); ++i) { 172 String attr = parser.getAttributeName(i); 173 String value = parser.getAttributeValue(i); 174 if (ATTR_TITLE.equals(attr)) { 175 title = value; 176 } else if (ATTR_POSTER_ART_URL.equals(attr)) { 177 posterArtUri = value; 178 } else if (ATTR_DURATION_SEC.equals(attr)) { 179 durationSec = Integer.parseInt(value); 180 } else if (ATTR_VIDEO_URL.equals(attr)) { 181 videoUrl = value; 182 } else if (ATTR_VIDEO_TYPE.equals(attr)) { 183 if (VALUE_VIDEO_TYPE_HTTP_PROGRESSIVE.equals(value)) { 184 videoType = TvInputPlayer.SOURCE_TYPE_HTTP_PROGRESSIVE; 185 } else if (VALUE_VIDEO_TYPE_HLS.equals(value)) { 186 videoType = TvInputPlayer.SOURCE_TYPE_HLS; 187 } else if (VALUE_VIDEO_TYPE_MPEG_DASH.equals(value)) { 188 videoType = TvInputPlayer.SOURCE_TYPE_MPEG_DASH; 189 } 190 } else if (ATTR_DESCRIPTION.equals(attr)) { 191 description = value; 192 } else if (ATTR_CONTENT_RATING.equals(attr)) { 193 contentRatings = value; 194 } 195 } 196 return new ProgramInfo(title, posterArtUri, description, durationSec, 197 TvContractUtils.stringToContentRatings(contentRatings), videoUrl, videoType, 0); 198 } 199 } 200