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.support.v17.leanback.widget.PlaybackSeekDataProvider; 223bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.support.v4.util.LruCache; 233bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.util.Log; 243bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport android.util.SparseArray; 253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 263bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport java.util.Iterator; 273bcad88cbf4488e747d84893c35f2351b8f84afeDake Guimport java.util.Map; 283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu/** 303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * 313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * Base class that implements PlaybackSeekDataProvider using AsyncTask.THREAD_POOL_EXECUTOR with 323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu * prefetching. 333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu */ 343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gupublic abstract class PlaybackSeekAsyncDataProvider extends PlaybackSeekDataProvider { 353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu static final String TAG = "SeekAsyncProvider"; 373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu long[] mSeekPositions; 393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // mCache is for the bitmap requested by user 403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu final LruCache<Integer, Bitmap> mCache; 413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // mPrefetchCache is for the bitmap not requested by user but prefetched by heuristic 423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // estimation. We use a different LruCache so that items in mCache will not be evicted by 433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // prefeteched items. 443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu final LruCache<Integer, Bitmap> mPrefetchCache; 453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu final SparseArray<LoadBitmapTask> mRequests = new SparseArray<>(); 463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu int mLastRequestedIndex = -1; 473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected boolean isCancelled(Object task) { 493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu return ((AsyncTask) task).isCancelled(); 503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected abstract Bitmap doInBackground(Object task, int index, long position); 533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu class LoadBitmapTask extends AsyncTask<Object, Object, Bitmap> { 553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu int mIndex; 573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu ResultCallback mResultCallback; 583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu LoadBitmapTask(int index, ResultCallback callback) { 603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mIndex = index; 613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mResultCallback = callback; 623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected Bitmap doInBackground(Object[] params) { 663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu return PlaybackSeekAsyncDataProvider.this 673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu .doInBackground(this, mIndex, mSeekPositions[mIndex]); 683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected void onPostExecute(Bitmap bitmap) { 723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mRequests.remove(mIndex); 733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Log.d(TAG, "thumb Loaded " + mIndex); 743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mResultCallback != null) { 753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mCache.put(mIndex, bitmap); 763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mResultCallback.onThumbnailLoaded(bitmap, mIndex); 773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } else { 783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.put(mIndex, bitmap); 793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public PlaybackSeekAsyncDataProvider() { 853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu this(16, 24); 863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public PlaybackSeekAsyncDataProvider(int cacheSize, int prefetchCacheSize) { 893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mCache = new LruCache<Integer, Bitmap>(cacheSize); 903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache = new LruCache<Integer, Bitmap>(prefetchCacheSize); 913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public void setSeekPositions(long[] positions) { 943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mSeekPositions = positions; 953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public long[] getSeekPositions() { 993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu return mSeekPositions; 1003bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1013bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 1023bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 1033bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public void getThumbnail(int index, ResultCallback callback) { 1043bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Integer key = index; 1053bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Bitmap bitmap = mCache.get(key); 1063bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (bitmap != null) { 1073bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu callback.onThumbnailLoaded(bitmap, index); 1083bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } else { 1093bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu bitmap = mPrefetchCache.get(key); 1103bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (bitmap != null) { 1113bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mCache.put(key, bitmap); 1123bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.remove(key); 1133bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu callback.onThumbnailLoaded(bitmap, index); 1143bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } else { 1153bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu LoadBitmapTask task = mRequests.get(index); 1163bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (task == null || task.isCancelled()) { 1173bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // no normal task or prefetch for the position, create a new task 1183bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task = new LoadBitmapTask(index, callback); 1193bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mRequests.put(index, task); 1203bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 1213bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } else { 1223bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu // update existing ResultCallback which might be normal task or prefetch 1233bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task.mResultCallback = callback; 1243bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1253bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1263bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1273bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mLastRequestedIndex != index) { 1283bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mLastRequestedIndex != -1) { 1293bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu prefetch(mLastRequestedIndex, index > mLastRequestedIndex); 1303bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1313bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mLastRequestedIndex = index; 1323bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1333bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1343bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 1353bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu protected void prefetch(int hintIndex, boolean forward) { 1363bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (Iterator<Map.Entry<Integer, Bitmap>> it = 1373bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.snapshot().entrySet().iterator(); it.hasNext(); ) { 1383bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Map.Entry<Integer, Bitmap> entry = it.next(); 1393bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (forward ? entry.getKey() < hintIndex : entry.getKey() > hintIndex) { 1403bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.remove(entry.getKey()); 1413bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1423bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1433bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu int inc = forward ? 1 : -1; 1443bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (int i = hintIndex; (mRequests.size() + mPrefetchCache.size() 1453bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu < mPrefetchCache.maxSize()) && (inc > 0 ? i < mSeekPositions.length : i >= 0); 1463bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu i += inc) { 1473bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Integer key = i; 1483bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mCache.get(key) == null && mPrefetchCache.get(key) == null) { 1493bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu LoadBitmapTask task = mRequests.get(i); 1503bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (task == null) { 1513bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task = new LoadBitmapTask(key, null); 1523bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mRequests.put(i, task); 1533bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 1543bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1553bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1563bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1573bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1583bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 1593bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 1603bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public void reset() { 1613bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (int i = 0; i < mRequests.size(); i++) { 1623bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu LoadBitmapTask task = mRequests.valueAt(i); 1633bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu task.cancel(true); 1643bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1653bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mRequests.clear(); 1663bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mCache.evictAll(); 1673bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mPrefetchCache.evictAll(); 1683bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu mLastRequestedIndex = -1; 1693bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1703bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu 1713bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu @Override 1723bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu public String toString() { 1733bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu StringBuilder b = new StringBuilder(); 1743bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append("Requests<"); 1753bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (int i = 0; i < mRequests.size(); i++) { 1763bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(mRequests.keyAt(i)); 1773bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(","); 1783bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1793bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append("> Cache<"); 1803bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (Iterator<Integer> it = mCache.snapshot().keySet().iterator(); it.hasNext();) { 1813bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Integer key = it.next(); 1823bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mCache.get(key) != null) { 1833bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(key); 1843bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(","); 1853bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1863bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1873bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(">"); 1883bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append("> PrefetchCache<"); 1893bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu for (Iterator<Integer> it = mPrefetchCache.snapshot().keySet().iterator(); it.hasNext();) { 1903bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu Integer key = it.next(); 1913bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu if (mPrefetchCache.get(key) != null) { 1923bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(key); 1933bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(","); 1943bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1953bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1963bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu b.append(">"); 1973bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu return b.toString(); 1983bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu } 1993bcad88cbf4488e747d84893c35f2351b8f84afeDake Gu} 200