3232import android .widget .LinearLayout ;
3333import android .widget .TextView ;
3434
35- import com .parse .ParseQuery . CachePolicy ;
35+ import com .parse .widget . util . ParseQueryPager ;
3636
3737import java .util .ArrayList ;
38- import java .util .Collections ;
3938import java .util .Iterator ;
4039import java .util .List ;
41- import java .util .Set ;
4240import java .util .WeakHashMap ;
43- import java .util .concurrent .ConcurrentHashMap ;
4441
45- import bolts .Capture ;
42+ import bolts .CancellationTokenSource ;
4643
4744/**
4845 * A {@code ParseQueryAdapter} handles the fetching of objects by page, and displaying objects as
@@ -112,15 +109,23 @@ public interface OnQueryLoadListener<T extends ParseObject> {
112109 void onLoaded (List <T > objects , Exception e );
113110 }
114111
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+
115123 // The key to use to display on the cell text label.
116124 private String textKey ;
117125
118126 // The key to use to fetch an image for display in the cell's image view.
119127 private String imageKey ;
120128
121- // The number of objects to show per page (default: 25)
122- private int objectsPerPage = 25 ;
123-
124129 // Whether the table should use the built-in pagination feature (default:
125130 // true)
126131 private boolean paginationEnabled = true ;
@@ -142,24 +147,6 @@ public interface OnQueryLoadListener<T extends ParseObject> {
142147
143148 private Context context ;
144149
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-
163150 private List <OnQueryLoadListener <T >> onQueryLoadListeners =
164151 new ArrayList <>();
165152
@@ -277,7 +264,7 @@ public ParseQueryAdapter(Context context, QueryFactory<T> queryFactory, int item
277264 private ParseQueryAdapter (Context context , QueryFactory <T > queryFactory , Integer itemViewResource ) {
278265 super ();
279266 this .context = context ;
280- this . queryFactory = queryFactory ;
267+ query = queryFactory . create () ;
281268 itemResourceId = itemViewResource ;
282269 }
283270
@@ -290,13 +277,32 @@ public Context getContext() {
290277 return context ;
291278 }
292279
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+ setPageOnQuery (page , query );
289+ return query ;
290+ }
291+ };
292+ cts = new CancellationTokenSource ();
293+ }
294+
295+ return pager ;
296+ }
297+ }
298+
293299 /** {@inheritDoc} **/
294300 @ Override
295301 public T getItem (int index ) {
296302 if (index == getPaginationCellRow ()) {
297303 return null ;
298304 }
299- return objects .get (index );
305+ return getPager (). getObjects () .get (index );
300306 }
301307
302308 /** {@inheritDoc} **/
@@ -337,18 +343,15 @@ public void unregisterDataSetObserver(DataSetObserver observer) {
337343 * Remove all elements from the list.
338344 */
339345 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 ();
346+ synchronized (lock ) {
347+ if (cts != null ) {
348+ cts .cancel ();
349+ }
350+ pager = null ;
351+ cts = null ;
350352 }
351- runningQueries .clear ();
353+
354+ notifyDataSetChanged ();
352355 }
353356
354357 /**
@@ -359,118 +362,45 @@ private void cancelAllQueries() {
359362 * {@code false}.
360363 */
361364 public void loadObjects () {
362- loadObjects ( 0 , true );
365+ loadNextPage ( true );
363366 }
364367
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 );
368+ private void loadNextPage (final boolean shouldClear ) {
369+ if (shouldClear && pager != null ) {
370+ cts .cancel ();
371+ pager = null ;
370372 }
371373
372374 notifyOnLoadingListeners ();
373375
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 >() {
376+ getPager ().loadNextPage (new FindCallback <T >() {
388377 @ Override
389- public void done (List <T > foundObjects , ParseException e ) {
390- if (! runningQueries . contains ( query )) {
378+ public void done (List <T > results , ParseException e ) {
379+ if (results == null && e == null ) { // cancelled
391380 return ;
392381 }
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- }
400382
383+ // Backwards compatibility
401384 if ((!Parse .isLocalDatastoreEnabled () &&
402- query .getCachePolicy () == CachePolicy .CACHE_ONLY ) &&
385+ query .getCachePolicy () == ParseQuery . CachePolicy .CACHE_ONLY ) &&
403386 (e != null ) && e .getCode () == ParseException .CACHE_MISS ) {
404387 // no-op on cache miss
405388 return ;
406389 }
407390
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 ;
427-
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 );
391+ notifyDataSetChanged ();
440392
441- syncObjectsWithPages ();
442-
443- // executes on the UI thread
444- notifyDataSetChanged ();
445- }
446-
447- notifyOnLoadedListeners (foundObjects , e );
393+ notifyOnLoadedListeners (results , e );
448394 }
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- }
395+ }, cts .getToken ());
461396 }
462397
463398 /**
464399 * Loads the next page of objects, appends to table, and notifies the UI that the model has
465400 * changed.
466401 */
467402 public void loadNextPage () {
468- if (objects .size () == 0 && runningQueries .size () == 0 ) {
469- loadObjects (0 , false );
470- }
471- else {
472- loadObjects (currentPage + 1 , false );
473- }
403+ loadNextPage (false );
474404 }
475405
476406 /**
@@ -482,7 +412,7 @@ public void loadNextPage() {
482412 */
483413 @ Override
484414 public int getCount () {
485- int count = objects .size ();
415+ int count = getPager (). getObjects () .size ();
486416
487417 if (shouldShowPaginationCell ()) {
488418 count ++;
@@ -689,7 +619,7 @@ public void setAutoload(boolean autoload) {
689619 return ;
690620 }
691621 this .autoload = autoload ;
692- if (this .autoload && !dataSetObservers .isEmpty () && objects .isEmpty ()) {
622+ if (this .autoload && !dataSetObservers .isEmpty () && getPager (). getObjects () .isEmpty ()) {
693623 loadObjects ();
694624 }
695625 }
@@ -725,11 +655,12 @@ private View getDefaultView(Context context) {
725655 }
726656
727657 private int getPaginationCellRow () {
728- return objects .size ();
658+ return getPager (). getObjects () .size ();
729659 }
730660
731661 private boolean shouldShowPaginationCell () {
732- return paginationEnabled && objects .size () > 0 && hasNextPage ;
662+ ParseQueryPager <T > pager = getPager ();
663+ return paginationEnabled && pager .getObjects ().size () > 0 && pager .hasNextPage ();
733664 }
734665
735666 private void notifyOnLoadingListeners () {
0 commit comments