3939import java .io .OutputStream ;
4040import java .util .ArrayList ;
4141import java .util .Arrays ;
42+ import java .util .Collections ;
43+ import java .util .Comparator ;
4244import java .util .HashSet ;
4345import java .util .List ;
4446import java .util .Queue ;
@@ -62,6 +64,8 @@ public class EpgSyncTask implements HtspMessage.Listener, Authenticator.Listener
6264 "eventAdd" , "eventUpdate" , "eventDelete" ,
6365 "initialSyncCompleted" ,
6466 }));
67+ private static final boolean IS_BRAVIA = Build .MODEL .contains ("BRAVIA" );
68+
6569 private static final String CHANNEL_ID_KEY = "channelId" ;
6670 private static final String CHANNEL_NUMBER_KEY = "channelNumber" ;
6771 private static final String CHANNEL_NUMBER_MINOR_KEY = "channelNumberMinor" ;
@@ -118,15 +122,27 @@ public interface Listener {
118122 private final SparseArray <Uri > mChannelUriMap ;
119123 private final SparseArray <Uri > mProgramUriMap ;
120124
121- private final SparseArray < ContentProviderOperation > mPendingChannelOps = new SparseArray <>();
122- private final SparseArray < ContentProviderOperation > mPendingProgramOps = new SparseArray <>();
125+ private final ArrayList < PendingChannelAddUpdate > mPendingChannelOps = new ArrayList <>();
126+ private final ArrayList < PendingEventAddUpdate > mPendingProgramOps = new ArrayList <>();
123127
124128 private final Queue <PendingChannelLogoFetch > mPendingChannelLogoFetches = new ConcurrentLinkedQueue <>();
125129 private final byte [] mImageBytes = new byte [102400 ];
126130
127131 private Set <Integer > mSeenChannels = new HashSet <>();
128132 private Set <Integer > mSeenPrograms = new HashSet <>();
129133
134+ private final class PendingChannelAddUpdate {
135+ public int channelId ;
136+ public int channelNumber ;
137+ public ContentProviderOperation operation ;
138+
139+ public PendingChannelAddUpdate (int channelId , int channelNumber , ContentProviderOperation operation ) {
140+ this .channelId = channelId ;
141+ this .channelNumber = channelNumber ;
142+ this .operation = operation ;
143+ }
144+ }
145+
130146 private final class PendingChannelLogoFetch {
131147 public int channelId ;
132148 public Uri logoUri ;
@@ -137,6 +153,16 @@ public PendingChannelLogoFetch(int channelId, Uri logoUri) {
137153 }
138154 }
139155
156+ private final class PendingEventAddUpdate {
157+ public int eventId ;
158+ public ContentProviderOperation operation ;
159+
160+ public PendingEventAddUpdate (int eventId , ContentProviderOperation operation ) {
161+ this .eventId = eventId ;
162+ this .operation = operation ;
163+ }
164+ }
165+
140166 public EpgSyncTask (Context context , @ NonNull HtspMessage .Dispatcher dispatcher , boolean quickSync ) {
141167 this (context , dispatcher );
142168
@@ -295,27 +321,32 @@ private void handleChannelAddUpdate(@NonNull HtspMessage message) {
295321 // Insert the channel
296322 if (Constants .DEBUG )
297323 Log .v (TAG , "Insert channel " + channelId );
298- mPendingChannelOps .put (
299- channelId ,
324+ final int channelNumber = message .getInteger (CHANNEL_NUMBER_KEY );
325+ mPendingChannelOps .add (new PendingChannelAddUpdate (
326+ channelId , channelNumber ,
300327 ContentProviderOperation .newInsert (TvContract .Channels .CONTENT_URI )
301- .withValues (values )
302- .build ()
303- );
328+ .withValues (values )
329+ .build ()
330+ )) ;
304331 } else {
305332 // Update the channel
306333 if (Constants .DEBUG )
307334 Log .v (TAG , "Update channel " + channelId );
308- mPendingChannelOps .put (
309- channelId ,
335+ mPendingChannelOps .add ( new PendingChannelAddUpdate (
336+ channelId , - 1 ,
310337 ContentProviderOperation .newUpdate (channelUri )
311338 .withValues (values )
312339 .build ()
313- );
340+ )) ;
314341 }
315342
316- // Throttle the batch operation not to cause TransactionTooLargeException. If the initial
317- // sync has already completed, flush for every message.
318- if (mInitialSyncCompleted || mPendingChannelOps .size () >= 500 ) {
343+ if (mInitialSyncCompleted ) {
344+ flushPendingChannelOps ();
345+ } else if (!IS_BRAVIA && mPendingChannelOps .size () >= 100 ) {
346+ // Throttle the batch operation not to cause TransactionTooLargeException. If the initial
347+ // sync has already completed, flush for every message. Additionally, we have no choice
348+ // on a Sony set but to not flush until we have all the channels, as sony's EPG view
349+ // is buggy.
319350 flushPendingChannelOps ();
320351 }
321352
@@ -333,10 +364,31 @@ private void flushPendingChannelOps() {
333364
334365 Log .d (TAG , "Flushing " + mPendingChannelOps .size () + " channel operations" );
335366
367+ if (IS_BRAVIA ) {
368+ // Sort the Pending Add/Updates by their channel numbers as Sony fails to do this in
369+ // their EPG view.
370+ Collections .sort (mPendingChannelOps , new Comparator <PendingChannelAddUpdate >() {
371+ @ Override
372+ public int compare (PendingChannelAddUpdate o1 , PendingChannelAddUpdate o2 ) {
373+ if (o1 .channelNumber == -1 || o2 .channelNumber == -1 ) {
374+ // One of the events is a channelUpdate, no need (or ability) to sort it
375+ return 0 ;
376+ }
377+ if (o1 .channelNumber > o2 .channelNumber ) {
378+ return 1 ;
379+ } else if (o1 .channelNumber == o2 .channelNumber ) {
380+ return 0 ;
381+ }
382+
383+ return -1 ;
384+ }
385+ });
386+ }
387+
336388 // Build out an ArrayList of Operations needed for applyBatch()
337389 ArrayList <ContentProviderOperation > operations = new ArrayList <>(mPendingChannelOps .size ());
338- for (int i = 0 ; i < mPendingChannelOps . size (); i ++ ) {
339- operations .add (mPendingChannelOps . valueAt ( i ) );
390+ for (PendingChannelAddUpdate pcau : mPendingChannelOps ) {
391+ operations .add (pcau . operation );
340392 }
341393
342394 // Apply the batch of Operations
@@ -360,7 +412,7 @@ private void flushPendingChannelOps() {
360412
361413 // Update the Channel Uri Map based on the results
362414 for (int i = 0 ; i < mPendingChannelOps .size (); i ++) {
363- final int channelId = mPendingChannelOps .keyAt (i );
415+ final int channelId = mPendingChannelOps .get (i ). channelId ;
364416 final ContentProviderResult result = results [i ];
365417
366418 mChannelUriMap .put (channelId , result .uri );
@@ -546,22 +598,22 @@ private void handleEventAddUpdate(@NonNull HtspMessage message) {
546598 // Insert the event
547599 if (Constants .DEBUG )
548600 Log .v (TAG , "Insert event " + eventId + " on channel " + channelId );
549- mPendingProgramOps .put (
601+ mPendingProgramOps .add ( new PendingEventAddUpdate (
550602 eventId ,
551603 ContentProviderOperation .newInsert (TvContract .Programs .CONTENT_URI )
552604 .withValues (values )
553605 .build ()
554- );
606+ )) ;
555607 } else {
556608 // Update the event
557609 if (Constants .DEBUG )
558610 Log .v (TAG , "Update event " + eventId + " on channel " + channelId );
559- mPendingProgramOps .put (
611+ mPendingProgramOps .add ( new PendingEventAddUpdate (
560612 eventId ,
561613 ContentProviderOperation .newUpdate (eventUri )
562614 .withValues (values )
563615 .build ()
564- );
616+ )) ;
565617 }
566618
567619 // Throttle the batch operation not to cause TransactionTooLargeException. If the initial
@@ -582,8 +634,8 @@ private void flushPendingEventOps() {
582634
583635 // Build out an ArrayList of Operations needed for applyBatch()
584636 ArrayList <ContentProviderOperation > operations = new ArrayList <>(mPendingProgramOps .size ());
585- for (int i = 0 ; i < mPendingProgramOps . size (); i ++ ) {
586- operations .add (mPendingProgramOps . valueAt ( i ) );
637+ for (PendingEventAddUpdate peau : mPendingProgramOps ) {
638+ operations .add (peau . operation );
587639 }
588640
589641 // Apply the batch of Operations
@@ -608,7 +660,7 @@ private void flushPendingEventOps() {
608660
609661 // Update the Event Uri Map based on the results
610662 for (int i = 0 ; i < mPendingProgramOps .size (); i ++) {
611- final int eventId = mPendingProgramOps .keyAt (i );
663+ final int eventId = mPendingProgramOps .get (i ). eventId ;
612664 final ContentProviderResult result = results [i ];
613665
614666 mProgramUriMap .put (eventId , result .uri );
0 commit comments