1954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey/* 2954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * Copyright (C) 2013 The Android Open Source Project 3954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * 4954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 5954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * you may not use this file except in compliance with the License. 6954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * You may obtain a copy of the License at 7954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * 8954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 9954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * 10954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 11954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 12954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * See the License for the specific language governing permissions and 14954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * limitations under the License. 15954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey */ 16954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 17954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeypackage com.android.externalstorage; 18954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 19954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.content.ContentResolver; 20a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkeyimport android.content.Context; 21a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkeyimport android.content.pm.ProviderInfo; 22de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.content.res.AssetFileDescriptor; 23954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.database.Cursor; 24954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.database.MatrixCursor; 25954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.database.MatrixCursor.RowBuilder; 26de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Bitmap; 27de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Bitmap.CompressFormat; 28de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Canvas; 29de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Color; 30de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Paint; 31de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.graphics.Point; 32954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.net.Uri; 33de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport android.os.AsyncTask; 34954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.Bundle; 35954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.CancellationSignal; 36954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.ParcelFileDescriptor; 37954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.os.SystemClock; 38954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract; 39954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract.Document; 40954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsContract.Root; 41954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.provider.DocumentsProvider; 42954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport android.util.Log; 43954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 44de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport libcore.io.IoUtils; 45de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport libcore.io.Streams; 46de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey 47de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.ByteArrayInputStream; 48de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.ByteArrayOutputStream; 49954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport java.io.FileNotFoundException; 50de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.FileOutputStream; 51de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkeyimport java.io.IOException; 52954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeyimport java.lang.ref.WeakReference; 53954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 54954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkeypublic class TestDocumentsProvider extends DocumentsProvider { 55954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String TAG = "TestDocuments"; 56954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 577aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean ROOTS_WEDGE = false; 587aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean ROOTS_LAG = false; 597aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean ROOTS_CRASH = false; 607aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean ROOTS_REFRESH = false; 61a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 627aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean DOCUMENT_CRASH = false; 637aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 647aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean RECENT_WEDGE = false; 657aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 667aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean CHILD_WEDGE = false; 677aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean CHILD_CRASH = false; 687aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 69d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey private static final boolean THUMB_HUNDREDS = false; 707aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean THUMB_WEDGE = false; 717aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey private static final boolean THUMB_CRASH = false; 72954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 73954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String MY_ROOT_ID = "myRoot"; 74954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String MY_DOC_ID = "myDoc"; 75954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String MY_DOC_NULL = "myNull"; 76954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 77954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String[] DEFAULT_ROOT_PROJECTION = new String[] { 786efba22ce510352bb84910d6efc42fecafd31ed7Jeff Sharkey Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_ICON, 79954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, 80954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Root.COLUMN_AVAILABLE_BYTES, 81954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey }; 82954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 83954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[] { 84954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME, 85954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE, 86954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey }; 87954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 88954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static String[] resolveRootProjection(String[] projection) { 89954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return projection != null ? projection : DEFAULT_ROOT_PROJECTION; 90954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 91954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 92954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static String[] resolveDocumentProjection(String[] projection) { 93954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION; 94954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 95954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 96a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey private String mAuthority; 97a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 98a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey @Override 99a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey public void attachInfo(Context context, ProviderInfo info) { 100a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey mAuthority = info.authority; 101a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey super.attachInfo(context, info); 102a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey } 103a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 104954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 105954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Cursor queryRoots(String[] projection) throws FileNotFoundException { 106a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey Log.d(TAG, "Someone asked for our roots!"); 107a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 1087aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (ROOTS_WEDGE) SystemClock.sleep(Integer.MAX_VALUE); 1097aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (ROOTS_LAG) SystemClock.sleep(3000); 1107aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (ROOTS_CRASH) System.exit(12); 111954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 1127aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (ROOTS_REFRESH) { 113a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey new AsyncTask<Void, Void, Void>() { 114a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey @Override 115a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey protected Void doInBackground(Void... params) { 116a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey SystemClock.sleep(3000); 117a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey Log.d(TAG, "Notifying that something changed!!"); 118a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey final Uri uri = DocumentsContract.buildRootsUri(mAuthority); 119a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey getContext().getContentResolver().notifyChange(uri, null, false); 120a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey return null; 121a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey } 122a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey }.execute(); 123a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey } 124a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey 125954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); 126954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final RowBuilder row = result.newRow(); 127b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_ROOT_ID, MY_ROOT_ID); 128b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_RECENTS); 129b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_TITLE, "_Test title which is really long"); 130a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey row.add(Root.COLUMN_SUMMARY, 131a9ce049db87259e302e2368d2a4a1c11a94fd831Jeff Sharkey SystemClock.elapsedRealtime() + " summary which is also super long text"); 132b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_DOCUMENT_ID, MY_DOC_ID); 133b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Root.COLUMN_AVAILABLE_BYTES, 1024); 134954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return result; 135954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 136954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 137954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 138954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Cursor queryDocument(String documentId, String[] projection) 139954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throws FileNotFoundException { 1407aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (DOCUMENT_CRASH) System.exit(12); 141954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 142954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); 143de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, documentId, 0); 144954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return result; 145954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 146954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 147954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey /** 148954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey * Holds any outstanding or finished "network" fetching. 149954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey */ 150954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private WeakReference<CloudTask> mTask; 151954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 152954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static class CloudTask implements Runnable { 153954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 154954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private final ContentResolver mResolver; 155954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private final Uri mNotifyUri; 156954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 157954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private volatile boolean mFinished; 158954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 159954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public CloudTask(ContentResolver resolver, Uri notifyUri) { 160954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mResolver = resolver; 161954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mNotifyUri = notifyUri; 162954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 163954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 164954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 165954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public void run() { 166954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Pretend to do some network 167954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, hashCode() + ": pretending to do some network!"); 168954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey SystemClock.sleep(2000); 169954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, hashCode() + ": network done!"); 170954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 171954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mFinished = true; 172954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 173954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Tell anyone remotely they should requery 174954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mResolver.notifyChange(mNotifyUri, null, false); 175954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 176954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 177954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public boolean includeIfFinished(MatrixCursor result) { 178954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, hashCode() + ": includeIfFinished() found " + mFinished); 179954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey if (mFinished) { 180de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile1", 0); 181de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile2", 0); 182de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile3", 0); 183de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile4", 0); 184de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile5", 0); 185de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "_networkfile6", 0); 186954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return true; 187954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } else { 188954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return false; 189954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 190954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 191954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 192954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 193954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey private static class CloudCursor extends MatrixCursor { 194954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Object keepAlive; 195954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public final Bundle extras = new Bundle(); 196954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 197954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public CloudCursor(String[] columnNames) { 198954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey super(columnNames); 199954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 200954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 201954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 202954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Bundle getExtras() { 203954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return extras; 204954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 205954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 206954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 207954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 208954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Cursor queryChildDocuments( 209954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey String parentDocumentId, String[] projection, String sortOrder) 210954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throws FileNotFoundException { 211954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 2127aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (CHILD_WEDGE) SystemClock.sleep(Integer.MAX_VALUE); 2137aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (CHILD_CRASH) System.exit(12); 2147aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 215954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final ContentResolver resolver = getContext().getContentResolver(); 216954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final Uri notifyUri = DocumentsContract.buildDocumentUri( 217954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey "com.example.documents", parentDocumentId); 218954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 219954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey CloudCursor result = new CloudCursor(resolveDocumentProjection(projection)); 220954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.setNotificationUri(resolver, notifyUri); 221954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 222954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Always include local results 223de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, MY_DOC_NULL, 0); 224de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "localfile1", 0); 225de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "localfile2", Document.FLAG_SUPPORTS_THUMBNAIL); 226de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "localfile3", 0); 227de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile(result, "localfile4", 0); 228954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 229d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey if (THUMB_HUNDREDS) { 230d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey for (int i = 0; i < 256; i++) { 231d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey includeFile(result, "i maded u an picshure", Document.FLAG_SUPPORTS_THUMBNAIL); 232d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 233d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 234d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey 235954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey synchronized (this) { 236954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Try picking up an existing network fetch 237954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey CloudTask task = mTask != null ? mTask.get() : null; 238954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey if (task == null) { 239954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, "No network task found; starting!"); 240954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey task = new CloudTask(resolver, notifyUri); 241954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey mTask = new WeakReference<CloudTask>(task); 242954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey new Thread(task).start(); 243954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 244954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Aggressively try freeing weak reference above 245954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey new Thread() { 246954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 247954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public void run() { 248954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey while (mTask.get() != null) { 249954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey SystemClock.sleep(200); 250954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey System.gc(); 251954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey System.runFinalization(); 252954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 253954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey Log.d(TAG, "AHA! THE CLOUD TASK WAS GC'ED!"); 254954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 255954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey }.start(); 256954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 257954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 258954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Blend in cloud results if ready 259954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey if (task.includeIfFinished(result)) { 260954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.extras.putString(DocumentsContract.EXTRA_INFO, 261954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey "Everything Went Better Than Expected and this message is quite " 262954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey + "long and verbose and maybe even too long"); 263954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.extras.putString(DocumentsContract.EXTRA_ERROR, 264954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey "But then again, maybe our server ran into an error, which means " 265954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey + "we're going to have a bad time"); 266954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } else { 267954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.extras.putBoolean(DocumentsContract.EXTRA_LOADING, true); 268954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 269954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 270954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Tie the network fetch to the cursor GC lifetime 271954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey result.keepAlive = task; 272954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 273954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return result; 274954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 275954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 276954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 277954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 278954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public Cursor queryRecentDocuments(String rootId, String[] projection) 279954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throws FileNotFoundException { 2807aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 2817aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (RECENT_WEDGE) SystemClock.sleep(Integer.MAX_VALUE); 2827aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 283954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // Pretend to take a super long time to respond 284954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey SystemClock.sleep(3000); 285954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 286954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); 287de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey includeFile( 288de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey result, "It was /worth/ the_wait for?the file:with the&incredibly long name", 0); 289954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return result; 290954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 291954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 292954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 293954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal) 294954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throws FileNotFoundException { 295954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey throw new FileNotFoundException(); 296954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 297954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 298954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey @Override 299de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey public AssetFileDescriptor openDocumentThumbnail( 300de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey String docId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { 3017aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 302d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey if (THUMB_WEDGE) wedgeUntilCanceled(signal); 3037aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey if (THUMB_CRASH) System.exit(12); 3047aa7601c09ab5d87cc15a0ed9a8f511d494a4cbcJeff Sharkey 305de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final Bitmap bitmap = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888); 306de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final Canvas canvas = new Canvas(bitmap); 307de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final Paint paint = new Paint(); 308de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey paint.setColor(Color.BLUE); 309de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey canvas.drawColor(Color.RED); 310de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey canvas.drawLine(0, 0, 32, 32, paint); 311de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey 312de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final ByteArrayOutputStream bos = new ByteArrayOutputStream(); 313de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey bitmap.compress(CompressFormat.JPEG, 50, bos); 314de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey 315de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); 316de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey try { 317de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createReliablePipe(); 318de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey new AsyncTask<Object, Object, Object>() { 319de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey @Override 320de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey protected Object doInBackground(Object... params) { 321de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey final FileOutputStream fos = new FileOutputStream(fds[1].getFileDescriptor()); 322de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey try { 323de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey Streams.copy(bis, fos); 324de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } catch (IOException e) { 325de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey throw new RuntimeException(e); 326de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } 327de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey IoUtils.closeQuietly(fds[1]); 328de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey return null; 329de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } 330de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); 331de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey return new AssetFileDescriptor(fds[0], 0, AssetFileDescriptor.UNKNOWN_LENGTH); 332de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } catch (IOException e) { 333de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey throw new FileNotFoundException(e.getMessage()); 334de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } 335de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey } 336de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey 337de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey @Override 338954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey public boolean onCreate() { 339954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey return true; 340954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 341954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 342d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey private static void wedgeUntilCanceled(CancellationSignal signal) { 343d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey if (signal != null) { 344d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey while (true) { 345d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey signal.throwIfCanceled(); 346d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey SystemClock.sleep(500); 347d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 348d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } else { 349d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey Log.w(TAG, "WEDGING WITHOUT A CANCELLATIONSIGNAL"); 350d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey SystemClock.sleep(Integer.MAX_VALUE); 351d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 352d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey } 353d01571e6d4e1c403534e19142720530d324eac9bJeff Sharkey 354de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey private static void includeFile(MatrixCursor result, String docId, int flags) { 355954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey final RowBuilder row = result.newRow(); 356b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_DOCUMENT_ID, docId); 357b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_DISPLAY_NAME, docId); 358b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_LAST_MODIFIED, System.currentTimeMillis()); 359de2b22fbc60b29dd8af60cf05862066c04559dc0Jeff Sharkey row.add(Document.COLUMN_FLAGS, flags); 360954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey 361954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey if (MY_DOC_ID.equals(docId)) { 362b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_MIME_TYPE, Document.MIME_TYPE_DIR); 363954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } else if (MY_DOC_NULL.equals(docId)) { 364954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey // No MIME type 365954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } else { 366b7757a6b32edea62a1a9a803ad83579220f26100Jeff Sharkey row.add(Document.COLUMN_MIME_TYPE, "application/octet-stream"); 367954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 368954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey } 369954be0232655d316bc5decbbd35579af902c75c2Jeff Sharkey} 370