124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik/* 2bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverette * Copyright 2018 The Android Open Source Project 324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * 424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * Licensed under the Apache License, Version 2.0 (the "License"); 524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * you may not use this file except in compliance with the License. 624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * You may obtain a copy of the License at 724418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * 824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * http://www.apache.org/licenses/LICENSE-2.0 924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * 1024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * Unless required by applicable law or agreed to in writing, software 1124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * distributed under the License is distributed on an "AS IS" BASIS, 1224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * See the License for the specific language governing permissions and 1424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik * limitations under the License. 1524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik */ 1624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 17bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viverettepackage androidx.paging; 1824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 19bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.annotation.AnyThread; 20bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.annotation.MainThread; 21bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.annotation.NonNull; 22bdc4c86d3dff74f6634a38e2f7b316b0e823a2c8Alan Viveretteimport androidx.annotation.Nullable; 2324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 245dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craikimport java.util.List; 2524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craikimport java.util.concurrent.Executor; 2624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 27e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craikclass ContiguousPagedList<K, V> extends PagedList<V> implements PagedStorage.Callback { 28e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik private final ContiguousDataSource<K, V> mDataSource; 2924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik private boolean mPrependWorkerRunning = false; 3024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik private boolean mAppendWorkerRunning = false; 3124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 3224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik private int mPrependItemsRequested = 0; 3324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik private int mAppendItemsRequested = 0; 3424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 355dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik private PageResult.Receiver<V> mReceiver = new PageResult.Receiver<V>() { 3667077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // Creation thread for initial synchronous load, otherwise main thread 3767077406223e49eba5ecd0def10ca80dd6909f16Chris Craik // Safe to access main thread only state - no other thread has reference during construction 3867077406223e49eba5ecd0def10ca80dd6909f16Chris Craik @AnyThread 39e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @Override 40a682a75615b3347502e1193a8e5b566a8edb5893Chris Craik public void onPageResult(@PageResult.ResultType int resultType, 41a682a75615b3347502e1193a8e5b566a8edb5893Chris Craik @NonNull PageResult<V> pageResult) { 425dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik if (pageResult.isInvalid()) { 43e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik detach(); 44e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik return; 45e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 4624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 47e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (isDetached()) { 48e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik // No op, have detached 49e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik return; 50e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 5124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 525dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik List<V> page = pageResult.page; 53a682a75615b3347502e1193a8e5b566a8edb5893Chris Craik if (resultType == PageResult.INIT) { 545dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik mStorage.init(pageResult.leadingNulls, page, pageResult.trailingNulls, 55e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik pageResult.positionOffset, ContiguousPagedList.this); 56e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik if (mLastLoad == LAST_LOAD_UNSPECIFIED) { 57e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik // Because the ContiguousPagedList wasn't initialized with a last load position, 58e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik // initialize it to the middle of the initial load 59e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik mLastLoad = 60e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik pageResult.leadingNulls + pageResult.positionOffset + page.size() / 2; 61e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik } 62a682a75615b3347502e1193a8e5b566a8edb5893Chris Craik } else if (resultType == PageResult.APPEND) { 635dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik mStorage.appendPage(page, ContiguousPagedList.this); 64a682a75615b3347502e1193a8e5b566a8edb5893Chris Craik } else if (resultType == PageResult.PREPEND) { 655dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik mStorage.prependPage(page, ContiguousPagedList.this); 6668d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik } else { 6768d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik throw new IllegalArgumentException("unexpected resultType " + resultType); 68e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 6967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 7068d6c3eb6419ab55acd9ed91b03eb2b25e47c55bChris Craik 7167077406223e49eba5ecd0def10ca80dd6909f16Chris Craik if (mBoundaryCallback != null) { 7267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik boolean deferEmpty = mStorage.size() == 0; 7367077406223e49eba5ecd0def10ca80dd6909f16Chris Craik boolean deferBegin = !deferEmpty 74a682a75615b3347502e1193a8e5b566a8edb5893Chris Craik && resultType == PageResult.PREPEND 755dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik && pageResult.page.size() == 0; 7667077406223e49eba5ecd0def10ca80dd6909f16Chris Craik boolean deferEnd = !deferEmpty 77a682a75615b3347502e1193a8e5b566a8edb5893Chris Craik && resultType == PageResult.APPEND 785dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik && pageResult.page.size() == 0; 7967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik deferBoundaryCallbacks(deferEmpty, deferBegin, deferEnd); 8067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik } 81e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 82e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik }; 83e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 84e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik static final int LAST_LOAD_UNSPECIFIED = -1; 85e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik 86e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik ContiguousPagedList( 87e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @NonNull ContiguousDataSource<K, V> dataSource, 8824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik @NonNull Executor mainThreadExecutor, 8924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik @NonNull Executor backgroundThreadExecutor, 9067077406223e49eba5ecd0def10ca80dd6909f16Chris Craik @Nullable BoundaryCallback<V> boundaryCallback, 91e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @NonNull Config config, 92e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik final @Nullable K key, 93e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik int lastLoad) { 945dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik super(new PagedStorage<V>(), mainThreadExecutor, backgroundThreadExecutor, 9567077406223e49eba5ecd0def10ca80dd6909f16Chris Craik boundaryCallback, config); 9624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik mDataSource = dataSource; 97e4cede1bc21d8f16cee3c0de2d77f70d7c3fd2cfChris Craik mLastLoad = lastLoad; 9824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 995dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik if (mDataSource.isInvalid()) { 1005dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik detach(); 1015dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } else { 102694588d1d059ac96142d6334ec7fce90abb7622bChris Craik mDataSource.dispatchLoadInitial(key, 1035dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik mConfig.initialLoadSizeHint, 104f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mConfig.pageSize, 1055dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik mConfig.enablePlaceholders, 106f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mMainThreadExecutor, 107f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mReceiver); 1085dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } 10924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 11024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 111e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @MainThread 11224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik @Override 113e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik void dispatchUpdatesSinceSnapshot( 114e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @NonNull PagedList<V> pagedListSnapshot, @NonNull Callback callback) { 1155dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik final PagedStorage<V> snapshot = pagedListSnapshot.mStorage; 116e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 117e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int newlyAppended = mStorage.getNumberAppended() - snapshot.getNumberAppended(); 118e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int newlyPrepended = mStorage.getNumberPrepended() - snapshot.getNumberPrepended(); 119e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 120e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int previousTrailing = snapshot.getTrailingNullCount(); 121e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int previousLeading = snapshot.getLeadingNullCount(); 122e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 123e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik // Validate that the snapshot looks like a previous version of this list - if it's not, 124e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik // we can't be sure we'll dispatch callbacks safely 1255dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik if (snapshot.isEmpty() 1265dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik || newlyAppended < 0 127e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik || newlyPrepended < 0 128e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik || mStorage.getTrailingNullCount() != Math.max(previousTrailing - newlyAppended, 0) 129e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik || mStorage.getLeadingNullCount() != Math.max(previousLeading - newlyPrepended, 0) 130e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik || (mStorage.getStorageCount() 131e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik != snapshot.getStorageCount() + newlyAppended + newlyPrepended)) { 132e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik throw new IllegalArgumentException("Invalid snapshot provided - doesn't appear" 133e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik + " to be a snapshot of this PagedList"); 134e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 135e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 136e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (newlyAppended != 0) { 137e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int changedCount = Math.min(previousTrailing, newlyAppended); 138e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int addedCount = newlyAppended - changedCount; 139e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 140e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int endPosition = snapshot.getLeadingNullCount() + snapshot.getStorageCount(); 141e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (changedCount != 0) { 142e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik callback.onChanged(endPosition, changedCount); 143e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 144e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (addedCount != 0) { 145e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik callback.onInserted(endPosition + changedCount, addedCount); 146e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 147e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 148e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (newlyPrepended != 0) { 149e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int changedCount = Math.min(previousLeading, newlyPrepended); 150e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int addedCount = newlyPrepended - changedCount; 151e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 152e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (changedCount != 0) { 153e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik callback.onChanged(previousLeading, changedCount); 154e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 155e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (addedCount != 0) { 156e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik callback.onInserted(0, addedCount); 157e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 1582e9d5136685b07ef5bfabcd3936b1eedb5d24e91Chris Craik } 1592e9d5136685b07ef5bfabcd3936b1eedb5d24e91Chris Craik } 1602e9d5136685b07ef5bfabcd3936b1eedb5d24e91Chris Craik 161e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @MainThread 1622e9d5136685b07ef5bfabcd3936b1eedb5d24e91Chris Craik @Override 163e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik protected void loadAroundInternal(int index) { 164504f54d29f6cbff7a520880bd885304def99127dChris Craik int prependItems = mConfig.prefetchDistance - (index - mStorage.getLeadingNullCount()); 165504f54d29f6cbff7a520880bd885304def99127dChris Craik int appendItems = index + mConfig.prefetchDistance 166e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik - (mStorage.getLeadingNullCount() + mStorage.getStorageCount()); 16724418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 168114bdca94b6571ef9f45ea6e826715141741d49eChris Craik mPrependItemsRequested = Math.max(prependItems, mPrependItemsRequested); 169114bdca94b6571ef9f45ea6e826715141741d49eChris Craik if (mPrependItemsRequested > 0) { 17024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik schedulePrepend(); 17124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 172114bdca94b6571ef9f45ea6e826715141741d49eChris Craik 173114bdca94b6571ef9f45ea6e826715141741d49eChris Craik mAppendItemsRequested = Math.max(appendItems, mAppendItemsRequested); 174114bdca94b6571ef9f45ea6e826715141741d49eChris Craik if (mAppendItemsRequested > 0) { 17524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik scheduleAppend(); 17624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 17724418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 17824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 17924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik @MainThread 18024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik private void schedulePrepend() { 18124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik if (mPrependWorkerRunning) { 18224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik return; 18324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 18424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik mPrependWorkerRunning = true; 18524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 186e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int position = mStorage.getLeadingNullCount() + mStorage.getPositionOffset(); 187e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 188e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik // safe to access first item here - mStorage can't be empty if we're prepending 18967077406223e49eba5ecd0def10ca80dd6909f16Chris Craik final V item = mStorage.getFirstLoadedItem(); 19024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik mBackgroundThreadExecutor.execute(new Runnable() { 19124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik @Override 19224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik public void run() { 193e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (isDetached()) { 19424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik return; 19524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 1965dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik if (mDataSource.isInvalid()) { 1975dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik detach(); 1985dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } else { 199694588d1d059ac96142d6334ec7fce90abb7622bChris Craik mDataSource.dispatchLoadBefore(position, item, mConfig.pageSize, 200f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mMainThreadExecutor, mReceiver); 2015dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } 2025dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik 20324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 20424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik }); 20524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 20624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 20724418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik @MainThread 20824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik private void scheduleAppend() { 20924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik if (mAppendWorkerRunning) { 21024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik return; 21124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 21224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik mAppendWorkerRunning = true; 21324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 214e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik final int position = mStorage.getLeadingNullCount() 215e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik + mStorage.getStorageCount() - 1 + mStorage.getPositionOffset(); 216e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik 217e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik // safe to access first item here - mStorage can't be empty if we're appending 21867077406223e49eba5ecd0def10ca80dd6909f16Chris Craik final V item = mStorage.getLastLoadedItem(); 21924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik mBackgroundThreadExecutor.execute(new Runnable() { 22024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik @Override 22124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik public void run() { 222e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik if (isDetached()) { 22324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik return; 22424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 2255dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik if (mDataSource.isInvalid()) { 2265dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik detach(); 2275dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } else { 228694588d1d059ac96142d6334ec7fce90abb7622bChris Craik mDataSource.dispatchLoadAfter(position, item, mConfig.pageSize, 229f174a1ccda73d8a42dc752a0b1c487508af2c54dChris Craik mMainThreadExecutor, mReceiver); 2305dc2fd49c2887578d8b76a9014e1b43d088c7fdaChris Craik } 23124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 23224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik }); 23324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 23424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 235e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @Override 236e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik boolean isContiguous() { 237e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik return true; 238e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 23924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 240ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik @NonNull 241ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik @Override 242ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik public DataSource<?, V> getDataSource() { 243ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik return mDataSource; 244ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik } 245ed6541a9e1b9737dc9aed9f46ad134fdb3d47a7cChris Craik 246e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @Nullable 247e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @Override 248e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik public Object getLastKey() { 249e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik return mDataSource.getKey(mLastLoad, mLastItem); 250e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 25124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 252e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @MainThread 253e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @Override 254e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik public void onInitialized(int count) { 255e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik notifyInserted(0, count); 256e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik } 25724418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 258e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @MainThread 259e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @Override 260e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik public void onPagePrepended(int leadingNulls, int changedCount, int addedCount) { 261e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik // consider whether to post more work, now that a page is fully prepended 262e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik mPrependItemsRequested = mPrependItemsRequested - changedCount - addedCount; 26324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik mPrependWorkerRunning = false; 26424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik if (mPrependItemsRequested > 0) { 26524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik // not done prepending, keep going 26624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik schedulePrepend(); 26724418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 26824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 26924418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik // finally dispatch callbacks, after prepend may have already been scheduled 270e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik notifyChanged(leadingNulls, changedCount); 271e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik notifyInserted(0, addedCount); 27267077406223e49eba5ecd0def10ca80dd6909f16Chris Craik 27367077406223e49eba5ecd0def10ca80dd6909f16Chris Craik offsetBoundaryAccessIndices(addedCount); 27424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 27524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 27624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik @MainThread 277e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @Override 278e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik public void onPageAppended(int endPosition, int changedCount, int addedCount) { 279e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik // consider whether to post more work, now that a page is fully appended 28024418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 281e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik mAppendItemsRequested = mAppendItemsRequested - changedCount - addedCount; 28224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik mAppendWorkerRunning = false; 28324418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik if (mAppendItemsRequested > 0) { 28424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik // not done appending, keep going 28524418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik scheduleAppend(); 28624418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 28724418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 28824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik // finally dispatch callbacks, after append may have already been scheduled 289e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik notifyChanged(endPosition, changedCount); 290e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik notifyInserted(endPosition + changedCount, addedCount); 29124418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 29224418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 293e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @MainThread 2942e9d5136685b07ef5bfabcd3936b1eedb5d24e91Chris Craik @Override 295e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik public void onPagePlaceholderInserted(int pageIndex) { 296e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik throw new IllegalStateException("Tiled callback on ContiguousPagedList"); 29724418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik } 29824418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik 299e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik @MainThread 300fd4fa4a65be59806d14e4625397948da008506b4Chris Craik @Override 301e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik public void onPageInserted(int start, int count) { 302e1178edf8a3082ca7dde8477bb43d001f67db11aChris Craik throw new IllegalStateException("Tiled callback on ContiguousPagedList"); 303fd4fa4a65be59806d14e4625397948da008506b4Chris Craik } 30424418e9aafa6ae3128ae47cf7087eda46dae4f5dChris Craik} 305