1/*
2 * Copyright (C) 2016 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
17package com.android.documentsui.picker;
18
19import static junit.framework.Assert.assertTrue;
20
21import static org.junit.Assert.assertEquals;
22import static org.junit.Assert.assertNotNull;
23
24import android.app.Activity;
25import android.content.ClipData;
26import android.content.Intent;
27import android.net.Uri;
28import android.os.AsyncTask;
29import android.provider.DocumentsContract;
30import android.provider.DocumentsContract.Path;
31import android.support.test.filters.MediumTest;
32import android.support.test.runner.AndroidJUnit4;
33
34import com.android.documentsui.AbstractActionHandler;
35import com.android.documentsui.R;
36import com.android.documentsui.base.DocumentStack;
37import com.android.documentsui.base.RootInfo;
38import com.android.documentsui.base.Shared;
39import com.android.documentsui.base.State;
40import com.android.documentsui.base.State.ActionType;
41import com.android.documentsui.testing.DocumentStackAsserts;
42import com.android.documentsui.testing.TestEnv;
43import com.android.documentsui.testing.TestProvidersAccess;
44import com.android.documentsui.testing.TestLastAccessedStorage;
45import com.android.documentsui.testing.TestResolveInfo;
46
47import org.junit.AfterClass;
48import org.junit.Before;
49import org.junit.Test;
50import org.junit.runner.RunWith;
51
52import java.util.Arrays;
53
54@RunWith(AndroidJUnit4.class)
55@MediumTest
56public class ActionHandlerTest {
57
58    private TestEnv mEnv;
59    private TestActivity mActivity;
60    private ActionHandler<TestActivity> mHandler;
61    private TestLastAccessedStorage mLastAccessed;
62
63    @Before
64    public void setUp() {
65        mEnv = TestEnv.create();
66        mActivity = TestActivity.create(mEnv);
67        mEnv.providers.configurePm(mActivity.packageMgr);
68        mLastAccessed = new TestLastAccessedStorage();
69
70        mHandler = new ActionHandler<>(
71                mActivity,
72                mEnv.state,
73                mEnv.providers,
74                mEnv.docs,
75                mEnv.searchViewManager,
76                mEnv::lookupExecutor,
77                mEnv.injector,
78                mLastAccessed
79        );
80
81        mEnv.dialogs.confirmNext();
82
83        mEnv.selectionMgr.toggleSelection("1");
84
85        AsyncTask.setDefaultExecutor(mEnv.mExecutor);
86    }
87
88    @AfterClass
89    public static void tearDownOnce() {
90        AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
91    }
92
93    @Test
94    public void testInitLocation_RestoresIfStackIsLoaded() throws Exception {
95        mEnv.state.stack.changeRoot(TestProvidersAccess.DOWNLOADS);
96        mEnv.state.stack.push(TestEnv.FOLDER_0);
97
98        mHandler.initLocation(mActivity.getIntent());
99        mActivity.restoreRootAndDirectory.assertCalled();
100    }
101
102    @Test
103    public void testInitLocation_LoadsRootDocIfStackOnlyHasRoot() throws Exception {
104        mEnv.state.stack.changeRoot(TestProvidersAccess.HAMMY);
105
106        mHandler.initLocation(mActivity.getIntent());
107        assertRootPicked(TestProvidersAccess.HAMMY.getUri());
108    }
109
110    @Test
111    public void testInitLocation_CopyDestination_DefaultsToDownloads() throws Exception {
112        mActivity.resources.bools.put(R.bool.show_documents_root, false);
113
114        Intent intent = mActivity.getIntent();
115        intent.setAction(Shared.ACTION_PICK_COPY_DESTINATION);
116        mHandler.initLocation(mActivity.getIntent());
117        assertRootPicked(TestProvidersAccess.DOWNLOADS.getUri());
118    }
119
120    @Test
121    public void testInitLocation_CopyDestination_DocumentsRootEnabled() throws Exception {
122        mActivity.resources.bools.put(R.bool.show_documents_root, true);
123        mActivity.resources.strings.put(R.string.default_root_uri, TestProvidersAccess.HOME.getUri().toString());
124
125        Intent intent = mActivity.getIntent();
126        intent.setAction(Shared.ACTION_PICK_COPY_DESTINATION);
127        mHandler.initLocation(intent);
128        assertRootPicked(TestProvidersAccess.HOME.getUri());
129    }
130
131    @Test
132    public void testInitLocation_LaunchToDocuments() throws Exception {
133        if (!mEnv.features.isLaunchToDocumentEnabled()) {
134            return;
135        }
136
137        mEnv.docs.nextIsDocumentsUri = true;
138        mEnv.docs.nextPath = new Path(
139                TestProvidersAccess.HOME.rootId,
140                Arrays.asList(
141                        TestEnv.FOLDER_0.documentId,
142                        TestEnv.FOLDER_1.documentId,
143                        TestEnv.FILE_GIF.documentId));
144        mEnv.docs.nextDocuments =
145                Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1, TestEnv.FILE_GIF);
146
147        mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
148        Intent intent = mActivity.getIntent();
149        intent.setAction(Intent.ACTION_GET_CONTENT);
150        intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, TestEnv.FILE_GIF.derivedUri);
151        mHandler.initLocation(intent);
152
153        mEnv.beforeAsserts();
154
155        DocumentStackAsserts.assertEqualsTo(mEnv.state.stack, TestProvidersAccess.HOME,
156                Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1));
157        mActivity.refreshCurrentRootAndDirectory.assertCalled();
158    }
159
160    @Test
161    public void testInitLocation_RestoresLastAccessedStack() throws Exception {
162        final DocumentStack stack =
163                new DocumentStack(TestProvidersAccess.HAMMY, TestEnv.FOLDER_0, TestEnv.FOLDER_1);
164        mLastAccessed.setLastAccessed(mActivity, stack);
165
166        mHandler.initLocation(mActivity.getIntent());
167
168        mEnv.beforeAsserts();
169        assertEquals(stack, mEnv.state.stack);
170        mActivity.refreshCurrentRootAndDirectory.assertCalled();
171    }
172
173    @Test
174    public void testInitLocation_DefaultToRecents_ActionGetContent() throws Exception {
175        testInitLocationDefaultToRecentsOnAction(State.ACTION_GET_CONTENT);
176    }
177
178    @Test
179    public void testInitLocation_DefaultToRecents_ActionOpen() throws Exception {
180        testInitLocationDefaultToRecentsOnAction(State.ACTION_OPEN);
181    }
182
183    @Test
184    public void testInitLocation_DefaultToRecents_ActionOpenTree() throws Exception {
185        testInitLocationDefaultToRecentsOnAction(State.ACTION_OPEN_TREE);
186    }
187
188    @Test
189    public void testInitLocation_DefaultsToDownloads_ActionCreate() throws Exception {
190        mEnv.state.action = State.ACTION_CREATE;
191        mActivity.resources.bools.put(R.bool.show_documents_root, false);
192
193        mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
194
195        mHandler.initLocation(mActivity.getIntent());
196
197        assertRootPicked(TestProvidersAccess.DOWNLOADS.getUri());
198    }
199
200    @Test
201    public void testOpenContainerDocument() {
202        mHandler.openContainerDocument(TestEnv.FOLDER_0);
203
204        assertEquals(TestEnv.FOLDER_0, mEnv.state.stack.peek());
205
206        mActivity.refreshCurrentRootAndDirectory.assertCalled();
207    }
208
209    @Test
210    public void testPickDocument_SetsCorrectResultAndFinishes_ActionPickCopyDestination()
211            throws Exception {
212
213        mEnv.state.action = State.ACTION_PICK_COPY_DESTINATION;
214        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
215        mEnv.state.stack.push(TestEnv.FOLDER_1);
216        mEnv.state.stack.push(TestEnv.FOLDER_2);
217
218        mActivity.finishedHandler.assertNotCalled();
219
220        mHandler.pickDocument(TestEnv.FOLDER_2);
221
222        mEnv.beforeAsserts();
223
224        assertLastAccessedStackUpdated();
225
226        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
227        final Intent result = mActivity.setResult.getLastValue().second;
228        assertPermission(result, Intent.FLAG_GRANT_READ_URI_PERMISSION, false);
229        assertPermission(result, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
230        assertPermission(result, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
231        assertPermission(result, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
232        assertContent(result, TestEnv.FOLDER_2.derivedUri);
233
234        mActivity.finishedHandler.assertCalled();
235    }
236
237    @Test
238    public void testPickDocument_SetsCorrectResultAndFinishes_ActionOpenTree() throws Exception {
239        mEnv.state.action = State.ACTION_OPEN_TREE;
240        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
241        mEnv.state.stack.push(TestEnv.FOLDER_1);
242        mEnv.state.stack.push(TestEnv.FOLDER_2);
243
244        mActivity.finishedHandler.assertNotCalled();
245
246        mHandler.pickDocument(TestEnv.FOLDER_2);
247
248        mEnv.beforeAsserts();
249
250        assertLastAccessedStackUpdated();
251
252        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
253        final Intent result = mActivity.setResult.getLastValue().second;
254        assertPermission(result, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
255        assertPermission(result, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
256        assertPermission(result, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
257        assertPermission(result, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, true);
258        assertContent(result, DocumentsContract.buildTreeDocumentUri(
259                TestProvidersAccess.HOME.authority, TestEnv.FOLDER_2.documentId));
260
261        mActivity.finishedHandler.assertCalled();
262    }
263
264    @Test
265    public void testSaveDocument_SetsCorrectResultAndFinishes() throws Exception {
266        mEnv.state.action = State.ACTION_CREATE;
267        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
268        mEnv.state.stack.push(TestEnv.FOLDER_1);
269
270        final String mimeType = "audio/aac";
271        final String displayName = "foobar.m4a";
272
273        mHandler.saveDocument(mimeType, displayName, (boolean inProgress) -> {});
274
275        mEnv.beforeAsserts();
276
277        mEnv.docs.assertCreatedDocument(TestEnv.FOLDER_1, mimeType, displayName);
278        final Uri docUri = mEnv.docs.getLastCreatedDocumentUri();
279
280        assertLastAccessedStackUpdated();
281
282        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
283        final Intent result = mActivity.setResult.getLastValue().second;
284        assertPermission(result, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
285        assertPermission(result, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
286        assertPermission(result, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
287        assertPermission(result, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
288        assertContent(result, docUri);
289
290        mActivity.finishedHandler.assertCalled();
291    }
292
293    @Test
294    public void testSaveDocument_ConfirmsOverwrite() {
295        if (!mEnv.features.isOverwriteConfirmationEnabled()) {
296            return;
297        }
298
299        mEnv.state.action = State.ACTION_CREATE;
300        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
301        mEnv.state.stack.push(TestEnv.FOLDER_1);
302
303        mHandler.saveDocument(null, TestEnv.FILE_JPG);
304
305        mEnv.dialogs.assertOverwriteConfirmed(TestEnv.FILE_JPG);
306    }
307
308    @Test
309    public void testFinishPicking_SetsCorrectResultAndFinishes_ActionGetContent() throws Exception {
310        mEnv.state.action = State.ACTION_GET_CONTENT;
311        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
312        mEnv.state.stack.push(TestEnv.FOLDER_1);
313
314        mActivity.finishedHandler.assertNotCalled();
315
316        mHandler.finishPicking(TestEnv.FILE_JPG.derivedUri);
317
318        mEnv.beforeAsserts();
319
320        assertLastAccessedStackUpdated();
321
322        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
323        final Intent result = mActivity.setResult.getLastValue().second;
324        assertPermission(result, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
325        assertPermission(result, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
326        assertPermission(result, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
327        assertPermission(result, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
328        assertContent(result, TestEnv.FILE_JPG.derivedUri);
329
330        mActivity.finishedHandler.assertCalled();
331    }
332
333    @Test
334    public void testFinishPicking_SetsCorrectResultAndFinishes_ActionGetContent_MultipleSelection()
335            throws Exception {
336        mEnv.state.action = State.ACTION_GET_CONTENT;
337        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
338        mEnv.state.stack.push(TestEnv.FOLDER_1);
339        mEnv.state.acceptMimes = new String[] { "image/*" };
340
341        mActivity.finishedHandler.assertNotCalled();
342
343        mHandler.finishPicking(TestEnv.FILE_JPG.derivedUri, TestEnv.FILE_GIF.derivedUri);
344
345        mEnv.beforeAsserts();
346
347        assertLastAccessedStackUpdated();
348
349        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
350        final Intent result = mActivity.setResult.getLastValue().second;
351        assertPermission(result, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
352        assertPermission(result, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
353        assertPermission(result, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, false);
354        assertPermission(result, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
355        assertContent(result, TestEnv.FILE_JPG.derivedUri, TestEnv.FILE_GIF.derivedUri);
356
357        mActivity.finishedHandler.assertCalled();
358    }
359
360    @Test
361    public void testFinishPicking_SetsCorrectResultAndFinishes_ActionOpen() throws Exception {
362        mEnv.state.action = State.ACTION_OPEN;
363        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
364        mEnv.state.stack.push(TestEnv.FOLDER_1);
365
366        mActivity.finishedHandler.assertNotCalled();
367
368        mHandler.finishPicking(TestEnv.FILE_JPG.derivedUri);
369
370        mEnv.beforeAsserts();
371
372        assertLastAccessedStackUpdated();
373
374        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
375        final Intent result = mActivity.setResult.getLastValue().second;
376        assertPermission(result, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
377        assertPermission(result, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
378        assertPermission(result, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
379        assertPermission(result, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
380        assertContent(result, TestEnv.FILE_JPG.derivedUri);
381
382        mActivity.finishedHandler.assertCalled();
383    }
384
385    @Test
386    public void testFinishPicking_SetsCorrectResultAndFinishes_ActionOpen_MultipleSelection()
387            throws Exception {
388        mEnv.state.action = State.ACTION_OPEN;
389        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
390        mEnv.state.stack.push(TestEnv.FOLDER_1);
391        mEnv.state.acceptMimes = new String[] { "image/*" };
392
393        mActivity.finishedHandler.assertNotCalled();
394
395        mHandler.finishPicking(TestEnv.FILE_JPG.derivedUri, TestEnv.FILE_GIF.derivedUri);
396
397        mEnv.beforeAsserts();
398
399        assertLastAccessedStackUpdated();
400
401        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
402        final Intent result = mActivity.setResult.getLastValue().second;
403        assertPermission(result, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
404        assertPermission(result, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
405        assertPermission(result, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
406        assertPermission(result, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
407        assertContent(result, TestEnv.FILE_JPG.derivedUri, TestEnv.FILE_GIF.derivedUri);
408
409        mActivity.finishedHandler.assertCalled();
410    }
411
412    @Test
413    public void testFinishPicking_SetsCorrectResultAndFinishes_ActionCreate() throws Exception {
414        mEnv.state.action = State.ACTION_CREATE;
415        mEnv.state.stack.changeRoot(TestProvidersAccess.HOME);
416        mEnv.state.stack.push(TestEnv.FOLDER_1);
417
418        mActivity.finishedHandler.assertNotCalled();
419
420        mHandler.finishPicking(TestEnv.FILE_JPG.derivedUri);
421
422        mEnv.beforeAsserts();
423
424        assertLastAccessedStackUpdated();
425
426        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
427        final Intent result = mActivity.setResult.getLastValue().second;
428        assertPermission(result, Intent.FLAG_GRANT_READ_URI_PERMISSION, true);
429        assertPermission(result, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, true);
430        assertPermission(result, Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION, true);
431        assertPermission(result, Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
432        assertContent(result, TestEnv.FILE_JPG.derivedUri);
433
434        mActivity.finishedHandler.assertCalled();
435    }
436
437    @Test
438    public void testOnAppPickedResult_OnOK() throws Exception {
439        Intent intent = new Intent();
440        mHandler.onActivityResult(AbstractActionHandler.CODE_FORWARD, Activity.RESULT_OK, intent);
441        mActivity.finishedHandler.assertCalled();
442        mActivity.setResult.assertCalled();
443
444        assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first);
445        assertEquals(intent, mActivity.setResult.getLastValue().second);
446    }
447
448    @Test
449    public void testOnAppPickedResult_OnNotOK() throws Exception {
450        Intent intent = new Intent();
451        mHandler.onActivityResult(0, Activity.RESULT_OK, intent);
452        mActivity.finishedHandler.assertNotCalled();
453        mActivity.setResult.assertNotCalled();
454
455        mHandler.onActivityResult(AbstractActionHandler.CODE_FORWARD, Activity.RESULT_CANCELED,
456                intent);
457        mActivity.finishedHandler.assertNotCalled();
458        mActivity.setResult.assertNotCalled();
459    }
460
461    @Test
462    public void testOpenAppRoot() throws Exception {
463        mHandler.openRoot(TestResolveInfo.create());
464        assertEquals((long) mActivity.startActivityForResult.getLastValue().second,
465                AbstractActionHandler.CODE_FORWARD);
466        assertNotNull(mActivity.startActivityForResult.getLastValue().first);
467    }
468
469    private void testInitLocationDefaultToRecentsOnAction(@ActionType int action)
470            throws Exception {
471        mEnv.state.action = action;
472
473        mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
474
475        mHandler.initLocation(mActivity.getIntent());
476
477        mEnv.beforeAsserts();
478        assertEquals(TestProvidersAccess.RECENTS, mEnv.state.stack.getRoot());
479        mActivity.refreshCurrentRootAndDirectory.assertCalled();
480    }
481
482    private void assertRootPicked(Uri expectedUri) throws Exception {
483        mEnv.beforeAsserts();
484
485        mActivity.rootPicked.assertCalled();
486        RootInfo root = mActivity.rootPicked.getLastValue();
487        assertNotNull(root);
488        assertEquals(expectedUri, root.getUri());
489    }
490
491    private void assertLastAccessedStackUpdated() {
492        assertEquals(mEnv.state.stack, mLastAccessed.getLastAccessed(
493                mActivity, mEnv.providers, mEnv.state));
494    }
495
496    private void assertPermission(Intent intent, int permission, boolean granted) {
497        int flags = intent.getFlags();
498
499        if (granted) {
500            assertEquals(permission, flags & permission);
501        } else {
502            assertEquals(0, flags & permission);
503        }
504    }
505
506    private void assertContent(Intent intent, Uri... contents) {
507        if (contents.length == 1) {
508            assertEquals(contents[0], intent.getData());
509        } else {
510            ClipData clipData = intent.getClipData();
511
512            assertNotNull(clipData);
513            for (int i = 0; i < mEnv.state.acceptMimes.length; ++i) {
514                assertEquals(mEnv.state.acceptMimes[i], clipData.getDescription().getMimeType(i));
515            }
516            for (int i = 0; i < contents.length; ++i) {
517                assertEquals(contents[i], clipData.getItemAt(i).getUri());
518            }
519        }
520    }
521}
522