1 /*
2  * Copyright (C) 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.android.tv.tuner.data;
18 
19 import android.support.annotation.NonNull;
20 import android.text.TextUtils;
21 import android.text.format.DateUtils;
22 import com.android.tv.common.util.StringUtils;
23 import com.android.tv.tuner.data.Track.AtscAudioTrack;
24 import com.android.tv.tuner.data.Track.AtscCaptionTrack;
25 import com.android.tv.tuner.util.ConvertUtils;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Locale;
30 
31 /** Collection of ATSC PSIP table items. */
32 public class PsipData {
33 
PsipData()34     private PsipData() {}
35 
36     public static class PsipSection {
37         private final int mTableId;
38         private final int mTableIdExtension;
39         private final int mSectionNumber;
40         private final boolean mCurrentNextIndicator;
41 
create(byte[] data)42         public static PsipSection create(byte[] data) {
43             if (data.length < 9) {
44                 return null;
45             }
46             int tableId = data[0] & 0xff;
47             int tableIdExtension = (data[3] & 0xff) << 8 | (data[4] & 0xff);
48             int sectionNumber = data[6] & 0xff;
49             boolean currentNextIndicator = (data[5] & 0x01) != 0;
50             return new PsipSection(tableId, tableIdExtension, sectionNumber, currentNextIndicator);
51         }
52 
PsipSection( int tableId, int tableIdExtension, int sectionNumber, boolean currentNextIndicator)53         private PsipSection(
54                 int tableId,
55                 int tableIdExtension,
56                 int sectionNumber,
57                 boolean currentNextIndicator) {
58             mTableId = tableId;
59             mTableIdExtension = tableIdExtension;
60             mSectionNumber = sectionNumber;
61             mCurrentNextIndicator = currentNextIndicator;
62         }
63 
getTableId()64         public int getTableId() {
65             return mTableId;
66         }
67 
getTableIdExtension()68         public int getTableIdExtension() {
69             return mTableIdExtension;
70         }
71 
getSectionNumber()72         public int getSectionNumber() {
73             return mSectionNumber;
74         }
75 
76         // This is for indicating that the section sent is applicable.
77         // We only consider a situation where currentNextIndicator is expected to have a true value.
78         // So, we are not going to compare this variable in hashCode() and equals() methods.
getCurrentNextIndicator()79         public boolean getCurrentNextIndicator() {
80             return mCurrentNextIndicator;
81         }
82 
83         @Override
hashCode()84         public int hashCode() {
85             int result = 17;
86             result = 31 * result + mTableId;
87             result = 31 * result + mTableIdExtension;
88             result = 31 * result + mSectionNumber;
89             return result;
90         }
91 
92         @Override
equals(Object obj)93         public boolean equals(Object obj) {
94             if (obj instanceof PsipSection) {
95                 PsipSection another = (PsipSection) obj;
96                 return mTableId == another.getTableId()
97                         && mTableIdExtension == another.getTableIdExtension()
98                         && mSectionNumber == another.getSectionNumber();
99             }
100             return false;
101         }
102     }
103 
104     /** {@link TvTracksInterface} for serving the audio and caption tracks. */
105     public interface TvTracksInterface {
106         /** Set the flag that tells the caption tracks have been found in this section container. */
setHasCaptionTrack()107         void setHasCaptionTrack();
108 
109         /**
110          * Returns whether or not the caption tracks have been found in this section container. If
111          * true, zero caption track will be interpreted as a clearance of the caption tracks.
112          */
hasCaptionTrack()113         boolean hasCaptionTrack();
114 
115         /** Returns the audio tracks received. */
getAudioTracks()116         List<AtscAudioTrack> getAudioTracks();
117 
118         /** Returns the caption tracks received. */
getCaptionTracks()119         List<AtscCaptionTrack> getCaptionTracks();
120     }
121 
122     public static class MgtItem {
123         public static final int TABLE_TYPE_EIT_RANGE_START = 0x0100;
124         public static final int TABLE_TYPE_EIT_RANGE_END = 0x017f;
125         public static final int TABLE_TYPE_CHANNEL_ETT = 0x0004;
126         public static final int TABLE_TYPE_ETT_RANGE_START = 0x0200;
127         public static final int TABLE_TYPE_ETT_RANGE_END = 0x027f;
128 
129         private final int mTableType;
130         private final int mTableTypePid;
131 
MgtItem(int tableType, int tableTypePid)132         public MgtItem(int tableType, int tableTypePid) {
133             mTableType = tableType;
134             mTableTypePid = tableTypePid;
135         }
136 
getTableType()137         public int getTableType() {
138             return mTableType;
139         }
140 
getTableTypePid()141         public int getTableTypePid() {
142             return mTableTypePid;
143         }
144     }
145 
146     public static class VctItem {
147         private final String mShortName;
148         private final String mLongName;
149         private final int mServiceType;
150         private final int mChannelTsid;
151         private final int mProgramNumber;
152         private final int mMajorChannelNumber;
153         private final int mMinorChannelNumber;
154         private final int mSourceId;
155         private String mDescription;
156 
VctItem( String shortName, String longName, int serviceType, int channelTsid, int programNumber, int majorChannelNumber, int minorChannelNumber, int sourceId)157         public VctItem(
158                 String shortName,
159                 String longName,
160                 int serviceType,
161                 int channelTsid,
162                 int programNumber,
163                 int majorChannelNumber,
164                 int minorChannelNumber,
165                 int sourceId) {
166             mShortName = shortName;
167             mLongName = longName;
168             mServiceType = serviceType;
169             mChannelTsid = channelTsid;
170             mProgramNumber = programNumber;
171             mMajorChannelNumber = majorChannelNumber;
172             mMinorChannelNumber = minorChannelNumber;
173             mSourceId = sourceId;
174         }
175 
getShortName()176         public String getShortName() {
177             return mShortName;
178         }
179 
getLongName()180         public String getLongName() {
181             return mLongName;
182         }
183 
getServiceType()184         public int getServiceType() {
185             return mServiceType;
186         }
187 
getChannelTsid()188         public int getChannelTsid() {
189             return mChannelTsid;
190         }
191 
getProgramNumber()192         public int getProgramNumber() {
193             return mProgramNumber;
194         }
195 
getMajorChannelNumber()196         public int getMajorChannelNumber() {
197             return mMajorChannelNumber;
198         }
199 
getMinorChannelNumber()200         public int getMinorChannelNumber() {
201             return mMinorChannelNumber;
202         }
203 
getSourceId()204         public int getSourceId() {
205             return mSourceId;
206         }
207 
208         @Override
toString()209         public String toString() {
210             return String.format(
211                     Locale.US,
212                     "ShortName: %s LongName: %s ServiceType: %d ChannelTsid: %x "
213                             + "ProgramNumber:%d %d-%d SourceId: %x",
214                     mShortName,
215                     mLongName,
216                     mServiceType,
217                     mChannelTsid,
218                     mProgramNumber,
219                     mMajorChannelNumber,
220                     mMinorChannelNumber,
221                     mSourceId);
222         }
223 
setDescription(String description)224         public void setDescription(String description) {
225             mDescription = description;
226         }
227 
getDescription()228         public String getDescription() {
229             return mDescription;
230         }
231     }
232 
233     public static class SdtItem {
234         private final String mServiceName;
235         private final String mServiceProviderName;
236         private final int mServiceType;
237         private final int mServiceId;
238         private final int mOriginalNetWorkId;
239 
SdtItem( String serviceName, String serviceProviderName, int serviceType, int serviceId, int originalNetWorkId)240         public SdtItem(
241                 String serviceName,
242                 String serviceProviderName,
243                 int serviceType,
244                 int serviceId,
245                 int originalNetWorkId) {
246             mServiceName = serviceName;
247             mServiceProviderName = serviceProviderName;
248             mServiceType = serviceType;
249             mServiceId = serviceId;
250             mOriginalNetWorkId = originalNetWorkId;
251         }
252 
getServiceName()253         public String getServiceName() {
254             return mServiceName;
255         }
256 
getServiceProviderName()257         public String getServiceProviderName() {
258             return mServiceProviderName;
259         }
260 
getServiceType()261         public int getServiceType() {
262             return mServiceType;
263         }
264 
getServiceId()265         public int getServiceId() {
266             return mServiceId;
267         }
268 
getOriginalNetworkId()269         public int getOriginalNetworkId() {
270             return mOriginalNetWorkId;
271         }
272 
273         @Override
toString()274         public String toString() {
275             return String.format(
276                     "ServiceName: %s ServiceProviderName:%s ServiceType:%d "
277                             + "OriginalNetworkId:%d",
278                     mServiceName, mServiceProviderName, mServiceType, mOriginalNetWorkId);
279         }
280     }
281 
282     /** A base class for descriptors of Ts packets. */
283     public abstract static class TsDescriptor {
getTag()284         public abstract int getTag();
285     }
286 
287     public static class ContentAdvisoryDescriptor extends TsDescriptor {
288         private final List<RatingRegion> mRatingRegions;
289 
ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions)290         public ContentAdvisoryDescriptor(List<RatingRegion> ratingRegions) {
291             mRatingRegions = ratingRegions;
292         }
293 
294         @Override
getTag()295         public int getTag() {
296             return SectionParser.DESCRIPTOR_TAG_CONTENT_ADVISORY;
297         }
298 
getRatingRegions()299         public List<RatingRegion> getRatingRegions() {
300             return mRatingRegions;
301         }
302     }
303 
304     public static class CaptionServiceDescriptor extends TsDescriptor {
305         private final List<AtscCaptionTrack> mCaptionTracks;
306 
CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks)307         public CaptionServiceDescriptor(List<AtscCaptionTrack> captionTracks) {
308             mCaptionTracks = captionTracks;
309         }
310 
311         @Override
getTag()312         public int getTag() {
313             return SectionParser.DESCRIPTOR_TAG_CAPTION_SERVICE;
314         }
315 
getCaptionTracks()316         public List<AtscCaptionTrack> getCaptionTracks() {
317             return mCaptionTracks;
318         }
319     }
320 
321     public static class ExtendedChannelNameDescriptor extends TsDescriptor {
322         private final String mLongChannelName;
323 
ExtendedChannelNameDescriptor(String longChannelName)324         public ExtendedChannelNameDescriptor(String longChannelName) {
325             mLongChannelName = longChannelName;
326         }
327 
328         @Override
getTag()329         public int getTag() {
330             return SectionParser.DESCRIPTOR_TAG_EXTENDED_CHANNEL_NAME;
331         }
332 
getLongChannelName()333         public String getLongChannelName() {
334             return mLongChannelName;
335         }
336     }
337 
338     public static class GenreDescriptor extends TsDescriptor {
339         private final String[] mBroadcastGenres;
340         private final String[] mCanonicalGenres;
341 
GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres)342         public GenreDescriptor(String[] broadcastGenres, String[] canonicalGenres) {
343             mBroadcastGenres = broadcastGenres;
344             mCanonicalGenres = canonicalGenres;
345         }
346 
347         @Override
getTag()348         public int getTag() {
349             return SectionParser.DESCRIPTOR_TAG_GENRE;
350         }
351 
getBroadcastGenres()352         public String[] getBroadcastGenres() {
353             return mBroadcastGenres;
354         }
355 
getCanonicalGenres()356         public String[] getCanonicalGenres() {
357             return mCanonicalGenres;
358         }
359     }
360 
361     public static class Ac3AudioDescriptor extends TsDescriptor {
362         // See A/52 Annex A. Table A4.2
363         private static final byte SAMPLE_RATE_CODE_48000HZ = 0;
364         private static final byte SAMPLE_RATE_CODE_44100HZ = 1;
365         private static final byte SAMPLE_RATE_CODE_32000HZ = 2;
366 
367         private final byte mSampleRateCode;
368         private final byte mBsid;
369         private final byte mBitRateCode;
370         private final byte mSurroundMode;
371         private final byte mBsmod;
372         private final int mNumChannels;
373         private final boolean mFullSvc;
374         private final byte mLangCod;
375         private final byte mLangCod2;
376         private final byte mMainId;
377         private final byte mPriority;
378         private final byte mAsvcflags;
379         private final String mText;
380         private final String mLanguage;
381         private final String mLanguage2;
382 
Ac3AudioDescriptor( byte sampleRateCode, byte bsid, byte bitRateCode, byte surroundMode, byte bsmod, int numChannels, boolean fullSvc, byte langCod, byte langCod2, byte mainId, byte priority, byte asvcflags, String text, String language, String language2)383         public Ac3AudioDescriptor(
384                 byte sampleRateCode,
385                 byte bsid,
386                 byte bitRateCode,
387                 byte surroundMode,
388                 byte bsmod,
389                 int numChannels,
390                 boolean fullSvc,
391                 byte langCod,
392                 byte langCod2,
393                 byte mainId,
394                 byte priority,
395                 byte asvcflags,
396                 String text,
397                 String language,
398                 String language2) {
399             mSampleRateCode = sampleRateCode;
400             mBsid = bsid;
401             mBitRateCode = bitRateCode;
402             mSurroundMode = surroundMode;
403             mBsmod = bsmod;
404             mNumChannels = numChannels;
405             mFullSvc = fullSvc;
406             mLangCod = langCod;
407             mLangCod2 = langCod2;
408             mMainId = mainId;
409             mPriority = priority;
410             mAsvcflags = asvcflags;
411             mText = text;
412             mLanguage = language;
413             mLanguage2 = language2;
414         }
415 
416         @Override
getTag()417         public int getTag() {
418             return SectionParser.DESCRIPTOR_TAG_AC3_AUDIO_STREAM;
419         }
420 
getSampleRateCode()421         public byte getSampleRateCode() {
422             return mSampleRateCode;
423         }
424 
getSampleRate()425         public int getSampleRate() {
426             switch (mSampleRateCode) {
427                 case SAMPLE_RATE_CODE_48000HZ:
428                     return 48000;
429                 case SAMPLE_RATE_CODE_44100HZ:
430                     return 44100;
431                 case SAMPLE_RATE_CODE_32000HZ:
432                     return 32000;
433                 default:
434                     return 0;
435             }
436         }
437 
getBsid()438         public byte getBsid() {
439             return mBsid;
440         }
441 
getBitRateCode()442         public byte getBitRateCode() {
443             return mBitRateCode;
444         }
445 
getSurroundMode()446         public byte getSurroundMode() {
447             return mSurroundMode;
448         }
449 
getBsmod()450         public byte getBsmod() {
451             return mBsmod;
452         }
453 
getNumChannels()454         public int getNumChannels() {
455             return mNumChannels;
456         }
457 
isFullSvc()458         public boolean isFullSvc() {
459             return mFullSvc;
460         }
461 
getLangCod()462         public byte getLangCod() {
463             return mLangCod;
464         }
465 
getLangCod2()466         public byte getLangCod2() {
467             return mLangCod2;
468         }
469 
getMainId()470         public byte getMainId() {
471             return mMainId;
472         }
473 
getPriority()474         public byte getPriority() {
475             return mPriority;
476         }
477 
getAsvcflags()478         public byte getAsvcflags() {
479             return mAsvcflags;
480         }
481 
getText()482         public String getText() {
483             return mText;
484         }
485 
getLanguage()486         public String getLanguage() {
487             return mLanguage;
488         }
489 
getLanguage2()490         public String getLanguage2() {
491             return mLanguage2;
492         }
493 
494         @Override
toString()495         public String toString() {
496             return String.format(
497                     Locale.US,
498                     "AC3 audio stream sampleRateCode: %d, bsid: %d, bitRateCode: %d, surroundMode:"
499                             + " %d, bsmod: %d, numChannels: %d, fullSvc: %s, langCod: %d, langCod2:"
500                             + " %d, mainId: %d, priority: %d, avcflags: %d, text: %s, language: %s,"
501                             + " language2: %s",
502                     mSampleRateCode,
503                     mBsid,
504                     mBitRateCode,
505                     mSurroundMode,
506                     mBsmod,
507                     mNumChannels,
508                     mFullSvc,
509                     mLangCod,
510                     mLangCod2,
511                     mMainId,
512                     mPriority,
513                     mAsvcflags,
514                     mText,
515                     mLanguage,
516                     mLanguage2);
517         }
518     }
519 
520     public static class Iso639LanguageDescriptor extends TsDescriptor {
521         private final List<AtscAudioTrack> mAudioTracks;
522 
Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks)523         public Iso639LanguageDescriptor(List<AtscAudioTrack> audioTracks) {
524             mAudioTracks = audioTracks;
525         }
526 
527         @Override
getTag()528         public int getTag() {
529             return SectionParser.DESCRIPTOR_TAG_ISO639LANGUAGE;
530         }
531 
getAudioTracks()532         public List<AtscAudioTrack> getAudioTracks() {
533             return mAudioTracks;
534         }
535 
536         @Override
toString()537         public String toString() {
538             return String.format("%s %s", getClass().getName(), mAudioTracks);
539         }
540     }
541 
542     public static class ServiceDescriptor extends TsDescriptor {
543         private final int mServiceType;
544         private final String mServiceProviderName;
545         private final String mServiceName;
546 
ServiceDescriptor(int serviceType, String serviceProviderName, String serviceName)547         public ServiceDescriptor(int serviceType, String serviceProviderName, String serviceName) {
548             mServiceType = serviceType;
549             mServiceProviderName = serviceProviderName;
550             mServiceName = serviceName;
551         }
552 
553         @Override
getTag()554         public int getTag() {
555             return SectionParser.DVB_DESCRIPTOR_TAG_SERVICE;
556         }
557 
getServiceType()558         public int getServiceType() {
559             return mServiceType;
560         }
561 
getServiceProviderName()562         public String getServiceProviderName() {
563             return mServiceProviderName;
564         }
565 
getServiceName()566         public String getServiceName() {
567             return mServiceName;
568         }
569 
570         @Override
toString()571         public String toString() {
572             return String.format(
573                     "Service descriptor, service type: %d, "
574                             + "service provider name: %s, "
575                             + "service name: %s",
576                     mServiceType, mServiceProviderName, mServiceName);
577         }
578     }
579 
580     public static class ShortEventDescriptor extends TsDescriptor {
581         private final String mLanguage;
582         private final String mEventName;
583         private final String mText;
584 
ShortEventDescriptor(String language, String eventName, String text)585         public ShortEventDescriptor(String language, String eventName, String text) {
586             mLanguage = language;
587             mEventName = eventName;
588             mText = text;
589         }
590 
getEventName()591         public String getEventName() {
592             return mEventName;
593         }
594 
595         @Override
getTag()596         public int getTag() {
597             return SectionParser.DVB_DESCRIPTOR_TAG_SHORT_EVENT;
598         }
599 
600         @Override
toString()601         public String toString() {
602             return String.format(
603                     "ShortEvent Descriptor, language:%s, event name: %s, " + "text:%s",
604                     mLanguage, mEventName, mText);
605         }
606     }
607 
608     public static class ParentalRatingDescriptor extends TsDescriptor {
609         private final HashMap<String, Integer> mRatings;
610 
ParentalRatingDescriptor(HashMap<String, Integer> ratings)611         public ParentalRatingDescriptor(HashMap<String, Integer> ratings) {
612             mRatings = ratings;
613         }
614 
615         @Override
getTag()616         public int getTag() {
617             return SectionParser.DVB_DESCRIPTOR_TAG_PARENTAL_RATING;
618         }
619 
getRatings()620         public HashMap<String, Integer> getRatings() {
621             return mRatings;
622         }
623 
624         @Override
toString()625         public String toString() {
626             return String.format("Parental rating descriptor, ratings:" + mRatings);
627         }
628     }
629 
630     public static class RatingRegion {
631         private final int mName;
632         private final String mDescription;
633         private final List<RegionalRating> mRegionalRatings;
634 
RatingRegion(int name, String description, List<RegionalRating> regionalRatings)635         public RatingRegion(int name, String description, List<RegionalRating> regionalRatings) {
636             mName = name;
637             mDescription = description;
638             mRegionalRatings = regionalRatings;
639         }
640 
getName()641         public int getName() {
642             return mName;
643         }
644 
getDescription()645         public String getDescription() {
646             return mDescription;
647         }
648 
getRegionalRatings()649         public List<RegionalRating> getRegionalRatings() {
650             return mRegionalRatings;
651         }
652     }
653 
654     public static class RegionalRating {
655         private final int mDimension;
656         private final int mRating;
657 
RegionalRating(int dimension, int rating)658         public RegionalRating(int dimension, int rating) {
659             mDimension = dimension;
660             mRating = rating;
661         }
662 
getDimension()663         public int getDimension() {
664             return mDimension;
665         }
666 
getRating()667         public int getRating() {
668             return mRating;
669         }
670     }
671 
672     public static class EitItem implements Comparable<EitItem>, TvTracksInterface {
673         public static final long INVALID_PROGRAM_ID = -1;
674 
675         // A program id is a primary key of TvContract.Programs table. So it must be positive.
676         private final long mProgramId;
677         private final int mEventId;
678         private final String mTitleText;
679         private String mDescription;
680         private final long mStartTime;
681         private final int mLengthInSecond;
682         private final String mContentRating;
683         private final List<AtscAudioTrack> mAudioTracks;
684         private final List<AtscCaptionTrack> mCaptionTracks;
685         private boolean mHasCaptionTrack;
686         private final String mBroadcastGenre;
687         private final String mCanonicalGenre;
688 
EitItem( long programId, int eventId, String titleText, long startTime, int lengthInSecond, String contentRating, List<AtscAudioTrack> audioTracks, List<AtscCaptionTrack> captionTracks, String broadcastGenre, String canonicalGenre, String description)689         public EitItem(
690                 long programId,
691                 int eventId,
692                 String titleText,
693                 long startTime,
694                 int lengthInSecond,
695                 String contentRating,
696                 List<AtscAudioTrack> audioTracks,
697                 List<AtscCaptionTrack> captionTracks,
698                 String broadcastGenre,
699                 String canonicalGenre,
700                 String description) {
701             mProgramId = programId;
702             mEventId = eventId;
703             mTitleText = titleText;
704             mStartTime = startTime;
705             mLengthInSecond = lengthInSecond;
706             mContentRating = contentRating;
707             mAudioTracks = audioTracks;
708             mCaptionTracks = captionTracks;
709             mBroadcastGenre = broadcastGenre;
710             mCanonicalGenre = canonicalGenre;
711             mDescription = description;
712         }
713 
getProgramId()714         public long getProgramId() {
715             return mProgramId;
716         }
717 
getEventId()718         public int getEventId() {
719             return mEventId;
720         }
721 
getTitleText()722         public String getTitleText() {
723             return mTitleText;
724         }
725 
setDescription(String description)726         public void setDescription(String description) {
727             mDescription = description;
728         }
729 
getDescription()730         public String getDescription() {
731             return mDescription;
732         }
733 
getStartTime()734         public long getStartTime() {
735             return mStartTime;
736         }
737 
getLengthInSecond()738         public int getLengthInSecond() {
739             return mLengthInSecond;
740         }
741 
getStartTimeUtcMillis()742         public long getStartTimeUtcMillis() {
743             return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime) * DateUtils.SECOND_IN_MILLIS;
744         }
745 
getEndTimeUtcMillis()746         public long getEndTimeUtcMillis() {
747             return ConvertUtils.convertGPSTimeToUnixEpoch(mStartTime + mLengthInSecond)
748                     * DateUtils.SECOND_IN_MILLIS;
749         }
750 
getContentRating()751         public String getContentRating() {
752             return mContentRating;
753         }
754 
755         @Override
getAudioTracks()756         public List<AtscAudioTrack> getAudioTracks() {
757             return mAudioTracks;
758         }
759 
760         @Override
getCaptionTracks()761         public List<AtscCaptionTrack> getCaptionTracks() {
762             return mCaptionTracks;
763         }
764 
getBroadcastGenre()765         public String getBroadcastGenre() {
766             return mBroadcastGenre;
767         }
768 
getCanonicalGenre()769         public String getCanonicalGenre() {
770             return mCanonicalGenre;
771         }
772 
773         @Override
setHasCaptionTrack()774         public void setHasCaptionTrack() {
775             mHasCaptionTrack = true;
776         }
777 
778         @Override
hasCaptionTrack()779         public boolean hasCaptionTrack() {
780             return mHasCaptionTrack;
781         }
782 
783         @Override
compareTo(@onNull EitItem item)784         public int compareTo(@NonNull EitItem item) {
785             // The list of caption tracks and the program ids are not compared in here because the
786             // channels in TIF have the concept of the caption and audio tracks while the programs
787             // do not and the programs in TIF only have a program id since they are the rows of
788             // Content Provider.
789             int ret = mEventId - item.getEventId();
790             if (ret != 0) {
791                 return ret;
792             }
793             ret = StringUtils.compare(mTitleText, item.getTitleText());
794             if (ret != 0) {
795                 return ret;
796             }
797             if (mStartTime > item.getStartTime()) {
798                 return 1;
799             } else if (mStartTime < item.getStartTime()) {
800                 return -1;
801             }
802             if (mLengthInSecond > item.getLengthInSecond()) {
803                 return 1;
804             } else if (mLengthInSecond < item.getLengthInSecond()) {
805                 return -1;
806             }
807 
808             // Compares content ratings
809             ret = StringUtils.compare(mContentRating, item.getContentRating());
810             if (ret != 0) {
811                 return ret;
812             }
813 
814             // Compares broadcast genres
815             ret = StringUtils.compare(mBroadcastGenre, item.getBroadcastGenre());
816             if (ret != 0) {
817                 return ret;
818             }
819             // Compares canonical genres
820             ret = StringUtils.compare(mCanonicalGenre, item.getCanonicalGenre());
821             if (ret != 0) {
822                 return ret;
823             }
824 
825             // Compares descriptions
826             return StringUtils.compare(mDescription, item.getDescription());
827         }
828 
getAudioLanguage()829         public String getAudioLanguage() {
830             if (mAudioTracks == null) {
831                 return "";
832             }
833             ArrayList<String> languages = new ArrayList<>();
834             for (AtscAudioTrack audioTrack : mAudioTracks) {
835                 languages.add(audioTrack.getLanguage());
836             }
837             return TextUtils.join(",", languages);
838         }
839 
840         @Override
toString()841         public String toString() {
842             return String.format(
843                     Locale.US,
844                     "EitItem programId: %d, eventId: %d, title: %s, startTime: %10d, "
845                             + "length: %6d, rating: %s, audio tracks: %d, caption tracks: %d, "
846                             + "genres (broadcast: %s, canonical: %s), description: %s",
847                     mProgramId,
848                     mEventId,
849                     mTitleText,
850                     mStartTime,
851                     mLengthInSecond,
852                     mContentRating,
853                     mAudioTracks != null ? mAudioTracks.size() : 0,
854                     mCaptionTracks != null ? mCaptionTracks.size() : 0,
855                     mBroadcastGenre,
856                     mCanonicalGenre,
857                     mDescription);
858         }
859     }
860 
861     public static class EttItem {
862         public final int eventId;
863         public final String text;
864 
EttItem(int eventId, String text)865         public EttItem(int eventId, String text) {
866             this.eventId = eventId;
867             this.text = text;
868         }
869     }
870 }
871