32
32
import android .widget .LinearLayout ;
33
33
import android .widget .TextView ;
34
34
35
- import com .parse .ParseQuery . CachePolicy ;
35
+ import com .parse .widget . util . ParseQueryPager ;
36
36
37
37
import java .util .ArrayList ;
38
- import java .util .Collections ;
39
38
import java .util .Iterator ;
40
39
import java .util .List ;
41
- import java .util .Set ;
42
40
import java .util .WeakHashMap ;
43
- import java .util .concurrent .ConcurrentHashMap ;
44
41
45
- import bolts .Capture ;
42
+ import bolts .CancellationTokenSource ;
46
43
47
44
/**
48
45
* A {@code ParseQueryAdapter} handles the fetching of objects by page, and displaying objects as
@@ -112,15 +109,23 @@ public interface OnQueryLoadListener<T extends ParseObject> {
112
109
void onLoaded (List <T > objects , Exception e );
113
110
}
114
111
112
+ private final Object lock = new Object ();
113
+ private ParseQueryPager <T > pager ;
114
+ private CancellationTokenSource cts ;
115
+
116
+ //region Backwards compatibility
117
+ private ParseQuery <T > query ;
118
+ private int objectsPerPage = 25 ;
119
+ //endregion
120
+
121
+ private Integer itemResourceId ;
122
+
115
123
// The key to use to display on the cell text label.
116
124
private String textKey ;
117
125
118
126
// The key to use to fetch an image for display in the cell's image view.
119
127
private String imageKey ;
120
128
121
- // The number of objects to show per page (default: 25)
122
- private int objectsPerPage = 25 ;
123
-
124
129
// Whether the table should use the built-in pagination feature (default:
125
130
// true)
126
131
private boolean paginationEnabled = true ;
@@ -142,24 +147,6 @@ public interface OnQueryLoadListener<T extends ParseObject> {
142
147
143
148
private Context context ;
144
149
145
- private List <T > objects = new ArrayList <>();
146
-
147
- private Set <ParseQuery > runningQueries =
148
- Collections .newSetFromMap (new ConcurrentHashMap <ParseQuery , Boolean >());
149
-
150
-
151
- // Used to keep track of the pages of objects when using CACHE_THEN_NETWORK. When using this,
152
- // the data will be flattened and put into the objects list.
153
- private List <List <T >> objectPages = new ArrayList <>();
154
-
155
- private int currentPage = 0 ;
156
-
157
- private Integer itemResourceId ;
158
-
159
- private boolean hasNextPage = true ;
160
-
161
- private QueryFactory <T > queryFactory ;
162
-
163
150
private List <OnQueryLoadListener <T >> onQueryLoadListeners =
164
151
new ArrayList <>();
165
152
@@ -277,7 +264,7 @@ public ParseQueryAdapter(Context context, QueryFactory<T> queryFactory, int item
277
264
private ParseQueryAdapter (Context context , QueryFactory <T > queryFactory , Integer itemViewResource ) {
278
265
super ();
279
266
this .context = context ;
280
- this . queryFactory = queryFactory ;
267
+ query = queryFactory . create () ;
281
268
itemResourceId = itemViewResource ;
282
269
}
283
270
@@ -290,13 +277,38 @@ public Context getContext() {
290
277
return context ;
291
278
}
292
279
280
+ private ParseQueryPager <T > getPager () {
281
+ synchronized (lock ) {
282
+ if (pager == null ) {
283
+ pager = new ParseQueryPager <T >(query , objectsPerPage ) {
284
+ @ Override
285
+ protected ParseQuery <T > createQuery (int page ) {
286
+ // Workaround for backwards compatibility
287
+ ParseQuery <T > query = new ParseQuery <>(getQuery ());
288
+ if (paginationEnabled ) {
289
+ setPageOnQuery (page , query );
290
+ }
291
+ return query ;
292
+ }
293
+ };
294
+ cts = new CancellationTokenSource ();
295
+ }
296
+
297
+ return pager ;
298
+ }
299
+ }
300
+
301
+ private List <T > getObjects () {
302
+ return getPager ().getObjects ();
303
+ }
304
+
293
305
/** {@inheritDoc} **/
294
306
@ Override
295
307
public T getItem (int index ) {
296
308
if (index == getPaginationCellRow ()) {
297
309
return null ;
298
310
}
299
- return objects .get (index );
311
+ return getObjects () .get (index );
300
312
}
301
313
302
314
/** {@inheritDoc} **/
@@ -337,18 +349,15 @@ public void unregisterDataSetObserver(DataSetObserver observer) {
337
349
* Remove all elements from the list.
338
350
*/
339
351
public void clear () {
340
- objectPages .clear ();
341
- cancelAllQueries ();
342
- syncObjectsWithPages ();
343
- notifyDataSetChanged ();
344
- currentPage = 0 ;
345
- }
346
-
347
- private void cancelAllQueries () {
348
- for (ParseQuery q : runningQueries ) {
349
- q .cancel ();
352
+ synchronized (lock ) {
353
+ if (cts != null ) {
354
+ cts .cancel ();
355
+ }
356
+ pager = null ;
357
+ cts = null ;
350
358
}
351
- runningQueries .clear ();
359
+
360
+ notifyDataSetChanged ();
352
361
}
353
362
354
363
/**
@@ -359,118 +368,47 @@ private void cancelAllQueries() {
359
368
* {@code false}.
360
369
*/
361
370
public void loadObjects () {
362
- loadObjects ( 0 , true );
371
+ loadNextPage ( true );
363
372
}
364
373
365
- private void loadObjects (final int page , final boolean shouldClear ) {
366
- final ParseQuery <T > query = queryFactory .create ();
367
-
368
- if (objectsPerPage > 0 && paginationEnabled ) {
369
- setPageOnQuery (page , query );
374
+ private void loadNextPage (final boolean shouldClear ) {
375
+ synchronized (lock ) {
376
+ if (shouldClear && pager != null ) {
377
+ cts .cancel ();
378
+ pager = null ;
379
+ }
370
380
}
371
381
372
382
notifyOnLoadingListeners ();
373
383
374
- // Create a new page
375
- if (page >= objectPages .size ()) {
376
- objectPages .add (page , new ArrayList <T >());
377
- }
378
-
379
- // In the case of CACHE_THEN_NETWORK, two callbacks will be called. Using this flag to keep
380
- // track of the callbacks.
381
- final Capture <Boolean > firstCallBack = new Capture <>(true );
382
-
383
- runningQueries .add (query );
384
-
385
- // TODO convert to Tasks and CancellationTokens
386
- // (depends on https://github.com/ParsePlatform/Parse-SDK-Android/issues/6)
387
- query .findInBackground (new FindCallback <T >() {
384
+ getPager ().loadNextPage (new FindCallback <T >() {
388
385
@ Override
389
- public void done (List <T > foundObjects , ParseException e ) {
390
- if (! runningQueries . contains ( query )) {
386
+ public void done (List <T > results , ParseException e ) {
387
+ if (results == null && e == null ) { // cancelled
391
388
return ;
392
389
}
393
- // In the case of CACHE_THEN_NETWORK, two callbacks will be called. We can only remove the
394
- // query after the second callback.
395
- if (Parse .isLocalDatastoreEnabled () ||
396
- (query .getCachePolicy () != CachePolicy .CACHE_THEN_NETWORK ) ||
397
- (query .getCachePolicy () == CachePolicy .CACHE_THEN_NETWORK && !firstCallBack .get ())) {
398
- runningQueries .remove (query );
399
- }
400
390
391
+ // Backwards compatibility
401
392
if ((!Parse .isLocalDatastoreEnabled () &&
402
- query .getCachePolicy () == CachePolicy .CACHE_ONLY ) &&
393
+ query .getCachePolicy () == ParseQuery . CachePolicy .CACHE_ONLY ) &&
403
394
(e != null ) && e .getCode () == ParseException .CACHE_MISS ) {
404
395
// no-op on cache miss
405
396
return ;
406
397
}
407
398
408
- if ((e != null ) &&
409
- ((e .getCode () == ParseException .CONNECTION_FAILED ) ||
410
- (e .getCode () != ParseException .CACHE_MISS ))) {
411
- hasNextPage = true ;
412
- } else if (foundObjects != null ) {
413
- if (shouldClear && firstCallBack .get ()) {
414
- runningQueries .remove (query );
415
- cancelAllQueries ();
416
- runningQueries .add (query ); // allow 2nd callback
417
- objectPages .clear ();
418
- objectPages .add (new ArrayList <T >());
419
- currentPage = page ;
420
- firstCallBack .set (false );
421
- }
422
-
423
- // Only advance the page, this prevents second call back from CACHE_THEN_NETWORK to
424
- // reset the page.
425
- if (page >= currentPage ) {
426
- currentPage = page ;
399
+ notifyDataSetChanged ();
427
400
428
- // since we set limit == objectsPerPage + 1
429
- hasNextPage = (foundObjects .size () > objectsPerPage );
430
- }
431
-
432
- if (paginationEnabled && foundObjects .size () > objectsPerPage ) {
433
- // Remove the last object, fetched in order to tell us whether there was a "next page"
434
- foundObjects .remove (objectsPerPage );
435
- }
436
-
437
- List <T > currentPage = objectPages .get (page );
438
- currentPage .clear ();
439
- currentPage .addAll (foundObjects );
440
-
441
- syncObjectsWithPages ();
442
-
443
- // executes on the UI thread
444
- notifyDataSetChanged ();
445
- }
446
-
447
- notifyOnLoadedListeners (foundObjects , e );
401
+ notifyOnLoadedListeners (results , e );
448
402
}
449
- });
450
- }
451
-
452
- /**
453
- * This is a helper function to sync the objects with objectPages. This is only used with the
454
- * CACHE_THEN_NETWORK option.
455
- */
456
- private void syncObjectsWithPages () {
457
- objects .clear ();
458
- for (List <T > pageOfObjects : objectPages ) {
459
- objects .addAll (pageOfObjects );
460
- }
403
+ }, cts .getToken ());
461
404
}
462
405
463
406
/**
464
407
* Loads the next page of objects, appends to table, and notifies the UI that the model has
465
408
* changed.
466
409
*/
467
410
public void loadNextPage () {
468
- if (objects .size () == 0 && runningQueries .size () == 0 ) {
469
- loadObjects (0 , false );
470
- }
471
- else {
472
- loadObjects (currentPage + 1 , false );
473
- }
411
+ loadNextPage (false );
474
412
}
475
413
476
414
/**
@@ -482,7 +420,7 @@ public void loadNextPage() {
482
420
*/
483
421
@ Override
484
422
public int getCount () {
485
- int count = objects .size ();
423
+ int count = getObjects () .size ();
486
424
487
425
if (shouldShowPaginationCell ()) {
488
426
count ++;
@@ -689,7 +627,7 @@ public void setAutoload(boolean autoload) {
689
627
return ;
690
628
}
691
629
this .autoload = autoload ;
692
- if (this .autoload && !dataSetObservers .isEmpty () && objects .isEmpty ()) {
630
+ if (this .autoload && !dataSetObservers .isEmpty () && getObjects () .isEmpty ()) {
693
631
loadObjects ();
694
632
}
695
633
}
@@ -725,11 +663,12 @@ private View getDefaultView(Context context) {
725
663
}
726
664
727
665
private int getPaginationCellRow () {
728
- return objects .size ();
666
+ return getObjects () .size ();
729
667
}
730
668
731
669
private boolean shouldShowPaginationCell () {
732
- return paginationEnabled && objects .size () > 0 && hasNextPage ;
670
+ ParseQueryPager <T > pager = getPager ();
671
+ return paginationEnabled && pager .getObjects ().size () > 0 && pager .hasNextPage ();
733
672
}
734
673
735
674
private void notifyOnLoadingListeners () {
0 commit comments