1251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey/* 2251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project 3251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * 4251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * you may not use this file except in compliance with the License. 6251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * You may obtain a copy of the License at 7251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * 8251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * 10251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * See the License for the specific language governing permissions and 14251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * limitations under the License. 15251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey */ 16251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 17251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeypackage com.android.documentsui; 18251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 19251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport static com.android.documentsui.DocumentsActivity.TAG; 20d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkeyimport static com.android.documentsui.DocumentsActivity.State.SORT_ORDER_LAST_MODIFIED; 21251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 227aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkeyimport android.app.ActivityManager; 23251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.content.AsyncTaskLoader; 24251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.content.ContentProviderClient; 25251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.content.Context; 26251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.database.Cursor; 277aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkeyimport android.database.MatrixCursor; 28251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.database.MergeCursor; 29251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.net.Uri; 306efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkeyimport android.os.Bundle; 31251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.provider.DocumentsContract; 32f6db154975ef575479ba4ab59d80bcf592288252Jeff Sharkeyimport android.provider.DocumentsContract.Document; 33251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.provider.DocumentsContract.Root; 346efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkeyimport android.text.format.DateUtils; 35251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport android.util.Log; 36251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 37251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport com.android.documentsui.DocumentsActivity.State; 38251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport com.android.documentsui.model.RootInfo; 39251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport com.google.android.collect.Maps; 40251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport com.google.common.collect.Lists; 41251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport com.google.common.util.concurrent.AbstractFuture; 42251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 43251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport libcore.io.IoUtils; 44251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 45251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport java.io.Closeable; 46251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport java.io.IOException; 47a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkeyimport java.util.Collection; 48251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport java.util.HashMap; 49251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport java.util.List; 50251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport java.util.concurrent.CountDownLatch; 51251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport java.util.concurrent.ExecutionException; 52d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkeyimport java.util.concurrent.Semaphore; 53251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeyimport java.util.concurrent.TimeUnit; 54251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 55251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkeypublic class RecentLoader extends AsyncTaskLoader<DirectoryResult> { 565545f56f7561810187545a1817b6001dd1f9931bJeff Sharkey private static final boolean LOGD = true; 57251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 580fb8556d38b1ae19e4021e5837353781326101ceJeff Sharkey // TODO: clean up cursor ownership so background thread doesn't traverse 590fb8556d38b1ae19e4021e5837353781326101ceJeff Sharkey // previously returned cursors for filtering/sorting; this currently races 600fb8556d38b1ae19e4021e5837353781326101ceJeff Sharkey // with the UI thread. 610fb8556d38b1ae19e4021e5837353781326101ceJeff Sharkey 627aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final int MAX_OUTSTANDING_RECENTS = 4; 637aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final int MAX_OUTSTANDING_RECENTS_SVELTE = 2; 64251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 65251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey /** 66251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey * Time to wait for first pass to complete before returning partial results. 67251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey */ 686efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey private static final int MAX_FIRST_PASS_WAIT_MILLIS = 500; 69251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 706efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey /** Maximum documents from a single root. */ 716efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey private static final int MAX_DOCS_FROM_ROOT = 64; 726efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey 736efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey /** Ignore documents older than this age. */ 746efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey private static final long REJECT_OLDER_THAN = 45 * DateUtils.DAY_IN_MILLIS; 756efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey 766efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey /** MIME types that should always be excluded from recents. */ 776efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey private static final String[] RECENT_REJECT_MIMES = new String[] { Document.MIME_TYPE_DIR }; 78251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 79d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey private final Semaphore mQueryPermits; 80251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 81a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey private final RootsCache mRoots; 82a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey private final State mState; 83348ad6866b91afa4d59d45df533ef88094c74d13Jeff Sharkey 84251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey private final HashMap<RootInfo, RecentTask> mTasks = Maps.newHashMap(); 85251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 86251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED; 87251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 88251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey private CountDownLatch mFirstPassLatch; 89251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey private volatile boolean mFirstPassDone; 90251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 91251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey private DirectoryResult mResult; 92251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 93251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey // TODO: create better transfer of ownership around cursor to ensure its 94251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey // closed in all edge cases. 95251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 96251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public class RecentTask extends AbstractFuture<Cursor> implements Runnable, Closeable { 97251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public final String authority; 98251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public final String rootId; 99251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 100251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey private Cursor mWithRoot; 101251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 102251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public RecentTask(String authority, String rootId) { 103251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey this.authority = authority; 104251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey this.rootId = rootId; 105251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 106251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 107251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 108251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public void run() { 109251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if (isCancelled()) return; 110251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 111d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey try { 112d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey mQueryPermits.acquire(); 113d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } catch (InterruptedException e) { 114d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey return; 115d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 116d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey 117d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey try { 118d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey runInternal(); 119d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } finally { 120d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey mQueryPermits.release(); 121d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 122d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 123d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey 124d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey public void runInternal() { 1257aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey ContentProviderClient client = null; 126251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey try { 1277aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey client = DocumentsApplication.acquireUnstableProviderOrThrow( 1287aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey getContext().getContentResolver(), authority); 1297aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 130251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey final Uri uri = DocumentsContract.buildRecentDocumentsUri(authority, rootId); 131251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey final Cursor cursor = client.query( 132251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey uri, null, null, null, DirectoryLoader.getQuerySortOrder(mSortOrder)); 133251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey mWithRoot = new RootCursorWrapper(authority, rootId, cursor, MAX_DOCS_FROM_ROOT); 134d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey 135251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } catch (Exception e) { 1367aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey Log.w(TAG, "Failed to load " + authority + ", " + rootId, e); 137251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } finally { 1387aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey ContentProviderClient.releaseQuietly(client); 1397aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey } 1407aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 1417aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey set(mWithRoot); 1427aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 1437aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey mFirstPassLatch.countDown(); 1447aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (mFirstPassDone) { 1457aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey onContentChanged(); 146251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 147251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 148251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 149251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 150251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public void close() throws IOException { 151251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey IoUtils.closeQuietly(mWithRoot); 152251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 153251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 154251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 155a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey public RecentLoader(Context context, RootsCache roots, State state) { 156251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey super(context); 157348ad6866b91afa4d59d45df533ef88094c74d13Jeff Sharkey mRoots = roots; 158a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey mState = state; 159d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey 160d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey // Keep clients around on high-RAM devices, since we'd be spinning them 161d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey // up moments later to fetch thumbnails anyway. 162d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey final ActivityManager am = (ActivityManager) getContext().getSystemService( 163d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey Context.ACTIVITY_SERVICE); 164d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey mQueryPermits = new Semaphore( 165d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey am.isLowRamDevice() ? MAX_OUTSTANDING_RECENTS_SVELTE : MAX_OUTSTANDING_RECENTS); 166251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 167251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 168251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 169251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public DirectoryResult loadInBackground() { 170251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if (mFirstPassLatch == null) { 171251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey // First time through we kick off all the recent tasks, and wait 172251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey // around to see if everyone finishes quickly. 173251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 174a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey final Collection<RootInfo> roots = mRoots.getMatchingRootsBlocking(mState); 175a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey for (RootInfo root : roots) { 176251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if ((root.flags & Root.FLAG_SUPPORTS_RECENTS) != 0) { 177251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey final RecentTask task = new RecentTask(root.authority, root.rootId); 178251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey mTasks.put(root, task); 179251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 180251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 181251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 182251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey mFirstPassLatch = new CountDownLatch(mTasks.size()); 183251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey for (RecentTask task : mTasks.values()) { 184d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey ProviderExecutor.forAuthority(task.authority).execute(task); 185251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 186251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 187251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey try { 188251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey mFirstPassLatch.await(MAX_FIRST_PASS_WAIT_MILLIS, TimeUnit.MILLISECONDS); 189251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey mFirstPassDone = true; 190251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } catch (InterruptedException e) { 191251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey throw new RuntimeException(e); 192251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 193251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 194251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 1956efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey final long rejectBefore = System.currentTimeMillis() - REJECT_OLDER_THAN; 1966efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey 197251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey // Collect all finished tasks 1987aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey boolean allDone = true; 199251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey List<Cursor> cursors = Lists.newArrayList(); 200251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey for (RecentTask task : mTasks.values()) { 201251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if (task.isDone()) { 202251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey try { 203d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey final Cursor cursor = task.get(); 2047aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (cursor == null) continue; 2057aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 206d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey final FilteringCursorWrapper filtered = new FilteringCursorWrapper( 2076efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey cursor, mState.acceptMimes, RECENT_REJECT_MIMES, rejectBefore) { 208d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey @Override 209d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey public void close() { 210d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey // Ignored, since we manage cursor lifecycle internally 211d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey } 212d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey }; 213d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey cursors.add(filtered); 214251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } catch (InterruptedException e) { 215251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey throw new RuntimeException(e); 216251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } catch (ExecutionException e) { 2177aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey // We already logged on other side 218251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 2197aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey } else { 2207aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey allDone = false; 221251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 222251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 223251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 2245545f56f7561810187545a1817b6001dd1f9931bJeff Sharkey if (LOGD) { 2255545f56f7561810187545a1817b6001dd1f9931bJeff Sharkey Log.d(TAG, "Found " + cursors.size() + " of " + mTasks.size() + " recent queries done"); 2265545f56f7561810187545a1817b6001dd1f9931bJeff Sharkey } 2275545f56f7561810187545a1817b6001dd1f9931bJeff Sharkey 228251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey final DirectoryResult result = new DirectoryResult(); 229d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey result.sortOrder = SORT_ORDER_LAST_MODIFIED; 230d182bb641f228b2d28527a6aa86075f6358ab838Jeff Sharkey 2316efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey // Hint to UI if we're still loading 2326efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey final Bundle extras = new Bundle(); 2337aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (!allDone) { 2346efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey extras.putBoolean(DocumentsContract.EXTRA_LOADING, true); 235251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 2366efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey 2377aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey final Cursor merged; 2387aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (cursors.size() > 0) { 2397aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey merged = new MergeCursor(cursors.toArray(new Cursor[cursors.size()])); 2407aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey } else { 2417aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey // Return something when nobody is ready 2427aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey merged = new MatrixCursor(new String[0]); 2437aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey } 2447aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 2456efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey final SortingCursorWrapper sorted = new SortingCursorWrapper(merged, result.sortOrder) { 2466efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey @Override 2476efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey public Bundle getExtras() { 2486efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey return extras; 2496efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey } 2506efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey }; 2516efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey 2526efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey result.cursor = sorted; 2536efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey 254251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey return result; 255251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 256251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 257251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 258251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public void cancelLoadInBackground() { 259251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey super.cancelLoadInBackground(); 260251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 261251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 262251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 263251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public void deliverResult(DirectoryResult result) { 264251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if (isReset()) { 265251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey IoUtils.closeQuietly(result); 266251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey return; 267251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 268251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey DirectoryResult oldResult = mResult; 269251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey mResult = result; 270251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 271251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if (isStarted()) { 272251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey super.deliverResult(result); 273251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 274251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 275251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if (oldResult != null && oldResult != result) { 276251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey IoUtils.closeQuietly(oldResult); 277251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 278251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 279251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 280251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 281251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey protected void onStartLoading() { 282251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if (mResult != null) { 283251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey deliverResult(mResult); 284251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 285251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey if (takeContentChanged() || mResult == null) { 286251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey forceLoad(); 287251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 288251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 289251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 290251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 291251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey protected void onStopLoading() { 292251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey cancelLoad(); 293251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 294251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 295251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 296251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey public void onCanceled(DirectoryResult result) { 297251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey IoUtils.closeQuietly(result); 298251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 299251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 300251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey @Override 301251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey protected void onReset() { 302251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey super.onReset(); 303251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 304251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey // Ensure the loader is stopped 305251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey onStopLoading(); 306251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 307251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey for (RecentTask task : mTasks.values()) { 308251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey IoUtils.closeQuietly(task); 309251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 310251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey 311251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey IoUtils.closeQuietly(mResult); 312251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey mResult = null; 313251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey } 314251097b3789632000ccdaf7fb7d66a82ff37d882Jeff Sharkey} 315