Home | History | Annotate | Download | only in documentclient
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.cts.documentclient;
     18 
     19 import android.app.Activity;
     20 import android.content.ContentResolver;
     21 import android.content.Intent;
     22 import android.content.IntentSender;
     23 import android.database.Cursor;
     24 import android.net.Uri;
     25 import android.os.Bundle;
     26 import android.os.SystemClock;
     27 import android.provider.DocumentsContract;
     28 import android.provider.DocumentsContract.Document;
     29 import android.provider.DocumentsContract.Path;
     30 import android.provider.DocumentsProvider;
     31 import android.support.test.uiautomator.UiObject;
     32 import android.support.test.uiautomator.UiObjectNotFoundException;
     33 import android.support.test.uiautomator.UiScrollable;
     34 import android.support.test.uiautomator.UiSelector;
     35 import android.test.MoreAsserts;
     36 import android.util.Log;
     37 
     38 import com.android.cts.documentclient.MyActivity.Result;
     39 
     40 import java.util.List;
     41 
     42 /**
     43  * Tests for {@link DocumentsProvider} and interaction with platform intents
     44  * like {@link Intent#ACTION_OPEN_DOCUMENT}.
     45  */
     46 public class DocumentsClientTest extends DocumentsClientTestCase {
     47     private static final String TAG = "DocumentsClientTest";
     48 
     49     private UiSelector findRootListSelector() throws UiObjectNotFoundException {
     50         return new UiSelector().resourceId(
     51                 getDocumentsUiPackageId() + ":id/container_roots").childSelector(
     52                 new UiSelector().resourceId(getDocumentsUiPackageId() + ":id/roots_list"));
     53 
     54     }
     55 
     56     private void revealRoot(UiSelector rootsList, String label) throws UiObjectNotFoundException {
     57         // We might need to expand drawer if not visible
     58         if (!new UiObject(rootsList).waitForExists(TIMEOUT)) {
     59             Log.d(TAG, "Failed to find roots list; trying to expand");
     60             final UiSelector hamburger = new UiSelector().resourceId(
     61                     getDocumentsUiPackageId() + ":id/toolbar").childSelector(
     62                     new UiSelector().className("android.widget.ImageButton").clickable(true));
     63             new UiObject(hamburger).click();
     64         }
     65 
     66         // Wait for the first list item to appear
     67         assertTrue("First list item",
     68                 new UiObject(rootsList.childSelector(new UiSelector())).waitForExists(TIMEOUT));
     69 
     70         // Now scroll around to find our item
     71         new UiScrollable(rootsList).scrollIntoView(new UiSelector().text(label));
     72     }
     73 
     74     private UiObject findSearchViewTextField() {
     75         final UiSelector selector = new UiSelector().resourceId(
     76                 getDocumentsUiPackageId() + ":id/option_menu_search").childSelector(
     77                 new UiSelector().resourceId(getDocumentsUiPackageId() + ":id/search_src_text"));
     78         return mDevice.findObject(selector);
     79     }
     80 
     81     private UiObject findRoot(String label) throws UiObjectNotFoundException {
     82         final UiSelector rootsList = findRootListSelector();
     83         revealRoot(rootsList, label);
     84 
     85         return new UiObject(rootsList.childSelector(new UiSelector().text(label)));
     86     }
     87 
     88     private UiObject findActionIcon(String rootLabel) throws UiObjectNotFoundException {
     89         final UiSelector rootsList = findRootListSelector();
     90         revealRoot(rootsList, rootLabel);
     91 
     92         final UiScrollable rootsListObject = new UiScrollable(rootsList);
     93         final UiObject rootItem = rootsListObject.getChildByText(
     94                 new UiSelector().className("android.widget.LinearLayout"), rootLabel, false);
     95         final UiSelector actionIcon =
     96                 new UiSelector().resourceId(getDocumentsUiPackageId() + ":id/action_icon_area");
     97         return new UiObject(rootItem.getSelector().childSelector(actionIcon));
     98     }
     99 
    100     private UiObject findDocument(String label) throws UiObjectNotFoundException {
    101         final UiSelector docList = new UiSelector().resourceId(
    102                 getDocumentsUiPackageId() + ":id/container_directory").childSelector(
    103                 new UiSelector().resourceId(getDocumentsUiPackageId() + ":id/dir_list"));
    104 
    105         // Wait for the first list item to appear
    106         assertTrue("First list item",
    107                 new UiObject(docList.childSelector(new UiSelector())).waitForExists(TIMEOUT));
    108 
    109         try {
    110             //Enfornce to set the list mode
    111             //Because UiScrollable can't reach the real bottom (when WEB_LINKABLE_FILE item) in grid mode when screen landscape mode
    112             new UiObject(new UiSelector().resourceId("com.android.documentsui:id/option_menu_list")).click();
    113             mDevice.waitForIdle();
    114         }catch (UiObjectNotFoundException e){
    115             //do nothing, already be in list mode.
    116         }
    117 
    118         // Now scroll around to find our item
    119         new UiScrollable(docList).scrollIntoView(new UiSelector().text(label));
    120         return new UiObject(docList.childSelector(new UiSelector().text(label)));
    121     }
    122 
    123     private UiObject findSaveButton() throws UiObjectNotFoundException {
    124         return new UiObject(new UiSelector().resourceId(
    125                 getDocumentsUiPackageId() + ":id/container_save")
    126                 .childSelector(new UiSelector().resourceId("android:id/button1")));
    127     }
    128 
    129     private UiObject findPositiveButton() throws UiObjectNotFoundException {
    130         return new UiObject(new UiSelector().resourceId("android:id/button1"));
    131     }
    132 
    133     public void testOpenSimple() throws Exception {
    134         if (!supportedHardware()) return;
    135 
    136         try {
    137             // Opening without permission should fail
    138             readFully(Uri.parse("content://com.android.cts.documentprovider/document/doc:file1"));
    139             fail("Able to read data before opened!");
    140         } catch (SecurityException expected) {
    141         }
    142 
    143         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    144         intent.addCategory(Intent.CATEGORY_OPENABLE);
    145         intent.setType("*/*");
    146         mActivity.startActivityForResult(intent, REQUEST_CODE);
    147 
    148         // Ensure that we see both of our roots
    149         mDevice.waitForIdle();
    150         assertTrue("CtsLocal root", findRoot("CtsLocal").exists());
    151         assertTrue("CtsCreate root", findRoot("CtsCreate").exists());
    152         assertFalse("CtsGetContent root", findRoot("CtsGetContent").exists());
    153 
    154         // Choose the local root.
    155         mDevice.waitForIdle();
    156         findRoot("CtsLocal").click();
    157 
    158         // Try picking a virtual file. Virtual files must not be returned for CATEGORY_OPENABLE
    159         // though, so the click should be ignored.
    160         mDevice.waitForIdle();
    161         findDocument("VIRTUAL_FILE").click();
    162         mDevice.waitForIdle();
    163 
    164         // Pick a regular file.
    165         mDevice.waitForIdle();
    166         findDocument("FILE1").click();
    167 
    168         // Confirm that the returned file is a regular file caused by the second click.
    169         final Result result = mActivity.getResult();
    170         final Uri uri = result.data.getData();
    171         assertEquals("doc:file1", DocumentsContract.getDocumentId(uri));
    172 
    173         // We should now have permission to read/write
    174         MoreAsserts.assertEquals("fileone".getBytes(), readFully(uri));
    175 
    176         writeFully(uri, "replaced!".getBytes());
    177         SystemClock.sleep(500);
    178         MoreAsserts.assertEquals("replaced!".getBytes(), readFully(uri));
    179     }
    180 
    181     public void testOpenVirtual() throws Exception {
    182         if (!supportedHardware()) return;
    183 
    184         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    185         intent.setType("*/*");
    186         mActivity.startActivityForResult(intent, REQUEST_CODE);
    187 
    188         // Pick a virtual file from the local root.
    189         mDevice.waitForIdle();
    190         findRoot("CtsLocal").click();
    191 
    192         mDevice.waitForIdle();
    193         findDocument("VIRTUAL_FILE").click();
    194 
    195         // Confirm that the returned file is actually the selected one.
    196         final Result result = mActivity.getResult();
    197         final Uri uri = result.data.getData();
    198         assertEquals("doc:virtual-file", DocumentsContract.getDocumentId(uri));
    199 
    200         final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
    201         final String streamTypes[] = resolver.getStreamTypes(uri, "*/*");
    202         assertEquals(1, streamTypes.length);
    203         assertEquals("text/plain", streamTypes[0]);
    204 
    205         // Virtual files are not readable unless an alternative MIME type is specified.
    206         try {
    207             readFully(uri);
    208             fail("Unexpected success in reading a virtual file. It should've failed.");
    209         } catch (IllegalArgumentException e) {
    210             // Expected.
    211         }
    212 
    213         // However, they are readable using an alternative MIME type from getStreamTypes().
    214         MoreAsserts.assertEquals(
    215                 "Converted contents.".getBytes(), readTypedFully(uri, streamTypes[0]));
    216     }
    217 
    218     public void testCreateNew() throws Exception {
    219         if (!supportedHardware()) return;
    220 
    221         final String DISPLAY_NAME = "My New Awesome Document Title";
    222         final String MIME_TYPE = "image/png";
    223 
    224         final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    225         intent.addCategory(Intent.CATEGORY_OPENABLE);
    226         intent.putExtra(Intent.EXTRA_TITLE, DISPLAY_NAME);
    227         intent.setType(MIME_TYPE);
    228         mActivity.startActivityForResult(intent, REQUEST_CODE);
    229 
    230         mDevice.waitForIdle();
    231         findRoot("CtsCreate").click();
    232 
    233         mDevice.waitForIdle();
    234         findSaveButton().click();
    235 
    236         final Result result = mActivity.getResult();
    237         final Uri uri = result.data.getData();
    238 
    239         writeFully(uri, "meow!".getBytes());
    240 
    241         assertEquals(DISPLAY_NAME, getColumn(uri, Document.COLUMN_DISPLAY_NAME));
    242         assertEquals(MIME_TYPE, getColumn(uri, Document.COLUMN_MIME_TYPE));
    243     }
    244 
    245     public void testCreateExisting() throws Exception {
    246         if (!supportedHardware()) return;
    247 
    248         final Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
    249         intent.addCategory(Intent.CATEGORY_OPENABLE);
    250         intent.putExtra(Intent.EXTRA_TITLE, "NEVERUSED");
    251         intent.setType("mime2/file2");
    252         mActivity.startActivityForResult(intent, REQUEST_CODE);
    253 
    254         mDevice.waitForIdle();
    255         findRoot("CtsCreate").click();
    256 
    257         // Pick file2, which should be selected since MIME matches, then try
    258         // picking a non-matching MIME, which should leave file2 selected.
    259         mDevice.waitForIdle();
    260         findDocument("FILE2").click();
    261         mDevice.waitForIdle();
    262         findDocument("FILE1").click();
    263 
    264         mDevice.waitForIdle();
    265         findSaveButton().click();
    266 
    267         mDevice.waitForIdle();
    268         findPositiveButton().click();
    269 
    270         final Result result = mActivity.getResult();
    271         final Uri uri = result.data.getData();
    272 
    273         MoreAsserts.assertEquals("filetwo".getBytes(), readFully(uri));
    274     }
    275 
    276     public void testTree() throws Exception {
    277         if (!supportedHardware()) return;
    278 
    279         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    280         mActivity.startActivityForResult(intent, REQUEST_CODE);
    281 
    282         mDevice.waitForIdle();
    283         findRoot("CtsCreate").click();
    284 
    285         mDevice.waitForIdle();
    286         findDocument("DIR2").click();
    287         mDevice.waitForIdle();
    288         findSaveButton().click();
    289         mDevice.waitForIdle();
    290         findPositiveButton().click();
    291 
    292         final Result result = mActivity.getResult();
    293         final Uri uri = result.data.getData();
    294 
    295         // We should have selected DIR2
    296         Uri doc = DocumentsContract.buildDocumentUriUsingTree(uri,
    297                 DocumentsContract.getTreeDocumentId(uri));
    298         Uri children = DocumentsContract.buildChildDocumentsUriUsingTree(uri,
    299                 DocumentsContract.getTreeDocumentId(uri));
    300 
    301         assertEquals("DIR2", getColumn(doc, Document.COLUMN_DISPLAY_NAME));
    302 
    303         // Look around and make sure we can see children
    304         final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
    305         Cursor cursor = resolver.query(children, new String[] {
    306                 Document.COLUMN_DISPLAY_NAME }, null, null, null);
    307         try {
    308             assertEquals(2, cursor.getCount());
    309             assertTrue(cursor.moveToFirst());
    310             assertEquals("FILE4", cursor.getString(0));
    311         } finally {
    312             cursor.close();
    313         }
    314 
    315         // Create some documents
    316         Uri pic = DocumentsContract.createDocument(resolver, doc, "image/png", "pic.png");
    317         Uri dir = DocumentsContract.createDocument(resolver, doc, Document.MIME_TYPE_DIR, "my dir");
    318         Uri dirPic = DocumentsContract.createDocument(resolver, dir, "image/png", "pic2.png");
    319 
    320         writeFully(pic, "pic".getBytes());
    321         writeFully(dirPic, "dirPic".getBytes());
    322 
    323         // Read then delete existing doc
    324         final Uri file4 = DocumentsContract.buildDocumentUriUsingTree(uri, "doc:file4");
    325         MoreAsserts.assertEquals("filefour".getBytes(), readFully(file4));
    326         assertTrue("delete", DocumentsContract.deleteDocument(resolver, file4));
    327         try {
    328             MoreAsserts.assertEquals("filefour".getBytes(), readFully(file4));
    329             fail("Expected file to be gone");
    330         } catch (SecurityException expected) {
    331         }
    332 
    333         // And rename something
    334         dirPic = DocumentsContract.renameDocument(resolver, dirPic, "wow");
    335         assertNotNull("rename", dirPic);
    336 
    337         // We should only see single child
    338         assertEquals("wow", getColumn(dirPic, Document.COLUMN_DISPLAY_NAME));
    339         MoreAsserts.assertEquals("dirPic".getBytes(), readFully(dirPic));
    340 
    341         try {
    342             // Make sure we can't see files outside selected dir
    343             getColumn(DocumentsContract.buildDocumentUriUsingTree(uri, "doc:file1"),
    344                     Document.COLUMN_DISPLAY_NAME);
    345             fail("Somehow read document outside tree!");
    346         } catch (SecurityException expected) {
    347         }
    348     }
    349 
    350     public void testGetContent_rootsShowing() throws Exception {
    351         if (!supportedHardware()) return;
    352 
    353         final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    354         intent.addCategory(Intent.CATEGORY_OPENABLE);
    355         intent.setType("*/*");
    356         mActivity.startActivityForResult(intent, REQUEST_CODE);
    357 
    358         // Look around, we should be able to see both DocumentsProviders and
    359         // other GET_CONTENT sources. If the DocumentsProvider and GetContent
    360         // root has the same package, they will be combined as one root item.
    361         mDevice.waitForIdle();
    362         assertTrue("CtsLocal root", findRoot("CtsLocal").exists());
    363         assertTrue("CtsCreate root", findRoot("CtsCreate").exists());
    364         assertFalse("CtsGetContent root", findRoot("CtsGetContent").exists());
    365 
    366         mDevice.waitForIdle();
    367         // Both CtsLocal and CtsLocal have action icon and have the same action.
    368         findActionIcon("CtsCreate");
    369         findActionIcon("CtsLocal").click();
    370         Result result = mActivity.getResult();
    371         assertEquals("ReSuLt", result.data.getAction());
    372     }
    373 
    374     public void testGetContentWithQuery_matchingFileShowing() throws Exception {
    375         if (!supportedHardware()) return;
    376 
    377         final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    378         intent.addCategory(Intent.CATEGORY_OPENABLE);
    379         intent.setType("*/*");
    380         final String queryString = "FILE2";
    381         intent.putExtra(Intent.EXTRA_CONTENT_QUERY, queryString);
    382         mActivity.startActivityForResult(intent, REQUEST_CODE);
    383 
    384         mDevice.waitForIdle();
    385 
    386         assertTrue(findDocument(queryString).exists());
    387 
    388         UiObject textField = findSearchViewTextField();
    389         assertTrue(textField.exists());
    390         assertEquals(queryString, textField.getText());
    391     }
    392 
    393     public void testGetContent_returnsResultToCallingActivity() throws Exception {
    394         if (!supportedHardware()) return;
    395 
    396         final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    397         intent.addCategory(Intent.CATEGORY_OPENABLE);
    398         intent.setType("*/*");
    399         mActivity.startActivityForResult(intent, REQUEST_CODE);
    400 
    401         mDevice.waitForIdle();
    402         findRoot("CtsCreate").click();
    403 
    404         // Pick the file.
    405         mDevice.waitForIdle();
    406         findDocument("FILE2").click();
    407 
    408         // Confirm that the returned file is a regular file caused by the click.
    409         final Result result = mActivity.getResult();
    410         final Uri uri = result.data.getData();
    411         assertEquals("doc:file2", DocumentsContract.getDocumentId(uri));
    412 
    413         // We should now have permission to read
    414         MoreAsserts.assertEquals("filetwo".getBytes(), readFully(uri));
    415     }
    416 
    417     public void testTransferDocument() throws Exception {
    418         if (!supportedHardware()) return;
    419 
    420         try {
    421             // Opening without permission should fail.
    422             readFully(Uri.parse("content://com.android.cts.documentprovider/document/doc:file1"));
    423             fail("Able to read data before opened!");
    424         } catch (SecurityException expected) {
    425         }
    426 
    427         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    428         mActivity.startActivityForResult(intent, REQUEST_CODE);
    429 
    430         mDevice.waitForIdle();
    431         findRoot("CtsCreate").click();
    432 
    433         findDocument("DIR2").click();
    434         mDevice.waitForIdle();
    435         findSaveButton().click();
    436         mDevice.waitForIdle();
    437         findPositiveButton().click();
    438 
    439         final Result result = mActivity.getResult();
    440         final Uri uri = result.data.getData();
    441 
    442         // We should have selected DIR2.
    443         final Uri docUri = DocumentsContract.buildDocumentUriUsingTree(uri,
    444                 DocumentsContract.getTreeDocumentId(uri));
    445 
    446         assertEquals("DIR2", getColumn(docUri, Document.COLUMN_DISPLAY_NAME));
    447 
    448         final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
    449         final Cursor cursor = resolver.query(
    450                 DocumentsContract.buildChildDocumentsUriUsingTree(
    451                         docUri, DocumentsContract.getDocumentId(docUri)),
    452                 new String[] { Document.COLUMN_DOCUMENT_ID, Document.COLUMN_DISPLAY_NAME,
    453                         Document.COLUMN_FLAGS },
    454                 null, null, null);
    455 
    456         Uri sourceFileUri = null;
    457         Uri targetDirUri = null;
    458 
    459         try {
    460             assertEquals(2, cursor.getCount());
    461             assertTrue(cursor.moveToFirst());
    462             sourceFileUri = DocumentsContract.buildDocumentUriUsingTree(
    463                     docUri, cursor.getString(0));
    464             assertEquals("FILE4", cursor.getString(1));
    465             assertEquals(Document.FLAG_SUPPORTS_WRITE |
    466                     Document.FLAG_SUPPORTS_COPY |
    467                     Document.FLAG_SUPPORTS_MOVE |
    468                     Document.FLAG_SUPPORTS_REMOVE, cursor.getInt(2));
    469 
    470             assertTrue(cursor.moveToNext());
    471             targetDirUri = DocumentsContract.buildDocumentUriUsingTree(
    472                     docUri, cursor.getString(0));
    473             assertEquals("SUB_DIR2", cursor.getString(1));
    474         } finally {
    475             cursor.close();
    476         }
    477 
    478         // Move, copy then remove.
    479         final Uri movedFileUri = DocumentsContract.moveDocument(
    480                 resolver, sourceFileUri, docUri, targetDirUri);
    481         assertTrue(movedFileUri != null);
    482         final Uri copiedFileUri = DocumentsContract.copyDocument(
    483                 resolver, movedFileUri, targetDirUri);
    484         assertTrue(copiedFileUri != null);
    485 
    486         // Confirm that the files are at the destinations.
    487         Cursor cursorDst = resolver.query(
    488                 DocumentsContract.buildChildDocumentsUriUsingTree(
    489                         targetDirUri, DocumentsContract.getDocumentId(targetDirUri)),
    490                 new String[] { Document.COLUMN_DOCUMENT_ID },
    491                 null, null, null);
    492         try {
    493             assertEquals(2, cursorDst.getCount());
    494             assertTrue(cursorDst.moveToFirst());
    495             assertEquals("doc:file4", cursorDst.getString(0));
    496             assertTrue(cursorDst.moveToNext());
    497             assertEquals("doc:file4_copy", cursorDst.getString(0));
    498         } finally {
    499             cursorDst.close();
    500         }
    501 
    502         // ... and gone from the source.
    503         final Cursor cursorSrc = resolver.query(
    504                 DocumentsContract.buildChildDocumentsUriUsingTree(
    505                         docUri, DocumentsContract.getDocumentId(docUri)),
    506                 new String[] { Document.COLUMN_DOCUMENT_ID },
    507                 null, null, null);
    508         try {
    509             assertEquals(1, cursorSrc.getCount());
    510             assertTrue(cursorSrc.moveToFirst());
    511             assertEquals("doc:sub_dir2", cursorSrc.getString(0));
    512         } finally {
    513             cursorSrc.close();
    514         }
    515 
    516         assertTrue(DocumentsContract.removeDocument(resolver, movedFileUri, targetDirUri));
    517         assertTrue(DocumentsContract.removeDocument(resolver, copiedFileUri, targetDirUri));
    518 
    519         // Finally, confirm that removing actually removed the files from the destination.
    520         cursorDst = resolver.query(
    521                 DocumentsContract.buildChildDocumentsUriUsingTree(
    522                         targetDirUri, DocumentsContract.getDocumentId(targetDirUri)),
    523                 new String[] { Document.COLUMN_DOCUMENT_ID },
    524                 null, null, null);
    525         try {
    526             assertEquals(0, cursorDst.getCount());
    527         } finally {
    528             cursorDst.close();
    529         }
    530     }
    531 
    532     public void testFindDocumentPathInScopedAccess() throws Exception {
    533         if (!supportedHardware()) return;
    534 
    535         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    536         mActivity.startActivityForResult(intent, REQUEST_CODE);
    537 
    538         mDevice.waitForIdle();
    539         findRoot("CtsCreate").click();
    540 
    541         mDevice.waitForIdle();
    542         findDocument("DIR2").click();
    543         mDevice.waitForIdle();
    544         findSaveButton().click();
    545         mDevice.waitForIdle();
    546         findPositiveButton().click();
    547 
    548         final Result result = mActivity.getResult();
    549         final Uri uri = result.data.getData();
    550 
    551         // We should have selected DIR2
    552         Uri doc = DocumentsContract.buildDocumentUriUsingTree(uri,
    553                 DocumentsContract.getTreeDocumentId(uri));
    554 
    555         assertEquals("DIR2", getColumn(doc, Document.COLUMN_DISPLAY_NAME));
    556 
    557         final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
    558 
    559         // Create some documents
    560         Uri dir = DocumentsContract.createDocument(resolver, doc, Document.MIME_TYPE_DIR, "my dir");
    561         Uri dirPic = DocumentsContract.createDocument(resolver, dir, "image/png", "pic2.png");
    562 
    563         writeFully(dirPic, "dirPic".getBytes());
    564 
    565         // Find the path of a document
    566         Path path = DocumentsContract.findDocumentPath(resolver, dirPic);
    567         assertNull(path.getRootId());
    568 
    569         final List<String> docs = path.getPath();
    570         assertEquals("Unexpected path: " + path, 3, docs.size());
    571         assertEquals(DocumentsContract.getTreeDocumentId(uri), docs.get(0));
    572         assertEquals(DocumentsContract.getDocumentId(dir), docs.get(1));
    573         assertEquals(DocumentsContract.getDocumentId(dirPic), docs.get(2));
    574     }
    575 
    576     public void testOpenDocumentAtInitialLocation() throws Exception {
    577         if (!supportedHardware()) return;
    578 
    579         // Clear DocsUI's storage to avoid it opening stored last location
    580         // which may make this test pass "luckily".
    581         clearDocumentsUi();
    582 
    583         final Uri docUri = DocumentsContract.buildDocumentUri(PROVIDER_PACKAGE, "doc:file1");
    584         final Intent intent = new Intent();
    585         intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
    586         intent.addCategory(Intent.CATEGORY_OPENABLE);
    587         intent.setType("*/*");
    588         intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, docUri);
    589         mActivity.startActivityForResult(intent, REQUEST_CODE);
    590         mDevice.waitForIdle();
    591 
    592         assertTrue(findDocument("FILE1").exists());
    593     }
    594 
    595     public void testOpenDocumentTreeAtInitialLocation() throws Exception {
    596         if (!supportedHardware()) return;
    597 
    598         // Clear DocsUI's storage to avoid it opening stored last location
    599         // which may make this test pass "luckily".
    600         clearDocumentsUi();
    601 
    602         final Uri docUri = DocumentsContract.buildDocumentUri(PROVIDER_PACKAGE, "doc:dir2");
    603         final Intent intent = new Intent();
    604         intent.setAction(Intent.ACTION_OPEN_DOCUMENT_TREE);
    605         intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, docUri);
    606         mActivity.startActivityForResult(intent, REQUEST_CODE);
    607         mDevice.waitForIdle();
    608 
    609         assertTrue(findDocument("FILE4").exists());
    610     }
    611 
    612     public void testOpenRootWithoutRootIdAtInitialLocation() throws Exception {
    613         if (!supportedHardware()) return;
    614 
    615         // Clear DocsUI's storage to avoid it opening stored last location
    616         // which may make this test pass "luckily".
    617         clearDocumentsUi();
    618 
    619         final Uri rootsUri = DocumentsContract.buildRootsUri(PROVIDER_PACKAGE);
    620         final Intent intent = new Intent(Intent.ACTION_VIEW);
    621         intent.setDataAndType(rootsUri, "vnd.android.document/root");
    622         mActivity.startActivity(intent);
    623         mDevice.waitForIdle();
    624 
    625         assertTrue(findDocument("DIR1").exists());
    626     }
    627 
    628     public void testCreateDocumentAtInitialLocation() throws Exception {
    629         if (!supportedHardware()) return;
    630 
    631         // Clear DocsUI's storage to avoid it opening stored last location
    632         // which may make this test pass "luckily".
    633         clearDocumentsUi();
    634 
    635         final Uri treeUri = DocumentsContract.buildTreeDocumentUri(PROVIDER_PACKAGE, "doc:local");
    636         final Uri docUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, "doc:file1");
    637         final Intent intent = new Intent();
    638         intent.setAction(Intent.ACTION_CREATE_DOCUMENT);
    639         intent.setType("plain/text");
    640         intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, docUri);
    641         mActivity.startActivityForResult(intent, REQUEST_CODE);
    642         mDevice.waitForIdle();
    643 
    644         assertTrue(findDocument("FILE1").exists());
    645     }
    646 
    647     public void testCreateWebLink() throws Exception {
    648         if (!supportedHardware()) return;
    649 
    650         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    651         intent.setType("*/*");
    652         mActivity.startActivityForResult(intent, REQUEST_CODE);
    653 
    654         // Pick a virtual file from the local root.
    655         mDevice.waitForIdle();
    656         findRoot("CtsLocal").click();
    657 
    658         mDevice.waitForIdle();
    659         findDocument("WEB_LINKABLE_FILE").click();
    660 
    661         // Confirm that the returned file is actually the selected one.
    662         final Result result = mActivity.getResult();
    663         final Uri uri = result.data.getData();
    664         assertEquals("doc:web-linkable-file", DocumentsContract.getDocumentId(uri));
    665 
    666         final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
    667 
    668         Bundle bundle = new Bundle();
    669         bundle.putStringArray(Intent.EXTRA_EMAIL, new String[] { "x (at) x.com" });
    670         final IntentSender intentSender = DocumentsContract.createWebLinkIntent(resolver,
    671                 uri, bundle);
    672 
    673         final int WEB_LINK_REQUEST_CODE = 1;
    674         mActivity.startIntentSenderForResult(intentSender, WEB_LINK_REQUEST_CODE,
    675                 null, 0, 0, 0);
    676         mDevice.waitForIdle();
    677 
    678         // Confirm the permissions dialog. The dialog is provided by the stub
    679         // provider.
    680         UiObject okButton = new UiObject(new UiSelector().resourceId("android:id/button1"));
    681         assertNotNull(okButton);
    682         assertTrue(okButton.waitForExists(TIMEOUT));
    683         okButton.click();
    684 
    685         final Result webLinkResult = mActivity.getResult();
    686         assertEquals(WEB_LINK_REQUEST_CODE, webLinkResult.requestCode);
    687         assertEquals(Activity.RESULT_OK, webLinkResult.resultCode);
    688 
    689         final Uri webLinkUri = webLinkResult.data.getData();
    690         assertEquals("http://www.foobar.com/shared/SW33TCH3RR13S", webLinkUri.toString());
    691     }
    692 
    693     public void testEject() throws Exception {
    694         if (!supportedHardware()) return;
    695 
    696         final Intent intent = new Intent(Intent.ACTION_VIEW);
    697         intent.addCategory(Intent.CATEGORY_DEFAULT);
    698         intent.setDataAndType(
    699                 DocumentsContract.buildChildDocumentsUri(PROVIDER_PACKAGE, "doc:dir1"),
    700                 Document.MIME_TYPE_DIR);
    701         mActivity.startActivity(intent);
    702 
    703         findActionIcon("eject").click();
    704 
    705         try {
    706             findRoot("eject").click();
    707             fail("Root eject was not ejected");
    708         } catch(UiObjectNotFoundException e) {
    709             // expected
    710         }
    711     }
    712 }
    713