13bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu/* 23bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * Copyright (C) 2017 The Android Open Source Project 33bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * 43bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * Licensed under the Apache License, Version 2.0 (the "License"); 53bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * you may not use this file except in compliance with the License. 63bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * You may obtain a copy of the License at 73bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * 83bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * http://www.apache.org/licenses/LICENSE-2.0 93bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * 103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * Unless required by applicable law or agreed to in writing, software 113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * distributed under the License is distributed on an "AS IS" BASIS, 123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * See the License for the specific language governing permissions and 143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * limitations under the License. 153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu */ 163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gupackage com.example.android.leanback; 183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 193bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.graphics.Bitmap; 203bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.os.AsyncTask; 213bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.util.Log; 223bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.util.SparseArray; 233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 24def582a5836579a3fadabfdbe4413cb1652bf098Aurimas Liutikasimport androidx.collection.LruCache; 25def582a5836579a3fadabfdbe4413cb1652bf098Aurimas Liutikasimport androidx.leanback.widget.PlaybackSeekDataProvider; 26def582a5836579a3fadabfdbe4413cb1652bf098Aurimas Liutikas 273bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport java.util.Iterator; 283bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport java.util.Map; 293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu/** 313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * 323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * Base class that implements PlaybackSeekDataProvider using AsyncTask.THREAD_POOL_EXECUTOR with 333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * prefetching. 343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu */ 353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gupublic abstract class PlaybackSeekAsyncDataProvider extends PlaybackSeekDataProvider { 363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu static final String TAG = "SeekAsyncProvider"; 383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu long[] mSeekPositions; 403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // mCache is for the bitmap requested by user 413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu final LruCache<Integer, Bitmap> mCache; 423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // mPrefetchCache is for the bitmap not requested by user but prefetched by heuristic 433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // estimation. We use a different LruCache so that items in mCache will not be evicted by 443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // prefeteched items. 453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu final LruCache<Integer, Bitmap> mPrefetchCache; 463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu final SparseArray<LoadBitmapTask> mRequests = new SparseArray<>(); 473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu int mLastRequestedIndex = -1; 483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected boolean isCancelled(Object task) { 503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu return ((AsyncTask) task).isCancelled(); 513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected abstract Bitmap doInBackground(Object task, int index, long position); 543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu class LoadBitmapTask extends AsyncTask<Object, Object, Bitmap> { 563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu int mIndex; 583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu ResultCallback mResultCallback; 593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu LoadBitmapTask(int index, ResultCallback callback) { 613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mIndex = index; 623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mResultCallback = callback; 633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected Bitmap doInBackground(Object[] params) { 673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu return PlaybackSeekAsyncDataProvider.this 683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu .doInBackground(this, mIndex, mSeekPositions[mIndex]); 693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected void onPostExecute(Bitmap bitmap) { 733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mRequests.remove(mIndex); 743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Log.d(TAG, "thumb Loaded " + mIndex); 753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mResultCallback != null) { 763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mCache.put(mIndex, bitmap); 773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mResultCallback.onThumbnailLoaded(bitmap, mIndex); 783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } else { 793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.put(mIndex, bitmap); 803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public PlaybackSeekAsyncDataProvider() { 863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu this(16, 24); 873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public PlaybackSeekAsyncDataProvider(int cacheSize, int prefetchCacheSize) { 903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mCache = new LruCache<Integer, Bitmap>(cacheSize); 913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache = new LruCache<Integer, Bitmap>(prefetchCacheSize); 923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public void setSeekPositions(long[] positions) { 953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mSeekPositions = positions; 963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public long[] getSeekPositions() { 1003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu return mSeekPositions; 1013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 1033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 1043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public void getThumbnail(int index, ResultCallback callback) { 1053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Integer key = index; 1063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Bitmap bitmap = mCache.get(key); 1073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (bitmap != null) { 1083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu callback.onThumbnailLoaded(bitmap, index); 1093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } else { 1103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu bitmap = mPrefetchCache.get(key); 1113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (bitmap != null) { 1123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mCache.put(key, bitmap); 1133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.remove(key); 1143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu callback.onThumbnailLoaded(bitmap, index); 1153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } else { 1163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu LoadBitmapTask task = mRequests.get(index); 1173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (task == null || task.isCancelled()) { 1183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // no normal task or prefetch for the position, create a new task 1193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task = new LoadBitmapTask(index, callback); 1203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mRequests.put(index, task); 1213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 1223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } else { 1233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // update existing ResultCallback which might be normal task or prefetch 1243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task.mResultCallback = callback; 1253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mLastRequestedIndex != index) { 1293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mLastRequestedIndex != -1) { 1303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu prefetch(mLastRequestedIndex, index > mLastRequestedIndex); 1313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mLastRequestedIndex = index; 1333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 1363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected void prefetch(int hintIndex, boolean forward) { 1373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (Iterator<Map.Entry<Integer, Bitmap>> it = 1383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.snapshot().entrySet().iterator(); it.hasNext(); ) { 1393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Map.Entry<Integer, Bitmap> entry = it.next(); 1403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (forward ? entry.getKey() < hintIndex : entry.getKey() > hintIndex) { 1413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.remove(entry.getKey()); 1423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu int inc = forward ? 1 : -1; 1453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (int i = hintIndex; (mRequests.size() + mPrefetchCache.size() 1463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu < mPrefetchCache.maxSize()) && (inc > 0 ? i < mSeekPositions.length : i >= 0); 1473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu i += inc) { 1483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Integer key = i; 1493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mCache.get(key) == null && mPrefetchCache.get(key) == null) { 1503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu LoadBitmapTask task = mRequests.get(i); 1513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (task == null) { 1523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task = new LoadBitmapTask(key, null); 1533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mRequests.put(i, task); 1543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 1553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 1603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 1613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public void reset() { 1623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (int i = 0; i < mRequests.size(); i++) { 1633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu LoadBitmapTask task = mRequests.valueAt(i); 1643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task.cancel(true); 1653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mRequests.clear(); 1673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mCache.evictAll(); 1683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.evictAll(); 1693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mLastRequestedIndex = -1; 1703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 1723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 1733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public String toString() { 1743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu StringBuilder b = new StringBuilder(); 1753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append("Requests<"); 1763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (int i = 0; i < mRequests.size(); i++) { 1773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(mRequests.keyAt(i)); 1783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(","); 1793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append("> Cache<"); 1813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (Iterator<Integer> it = mCache.snapshot().keySet().iterator(); it.hasNext();) { 1823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Integer key = it.next(); 1833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mCache.get(key) != null) { 1843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(key); 1853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(","); 1863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(">"); 1893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append("> PrefetchCache<"); 1903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (Iterator<Integer> it = mPrefetchCache.snapshot().keySet().iterator(); it.hasNext();) { 1913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Integer key = it.next(); 1923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mPrefetchCache.get(key) != null) { 1933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(key); 1943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(","); 1953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(">"); 1983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu return b.toString(); 1993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 2003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu} 201