diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java index 67db9ee9ac473bd4368b66a1de84955485281b9d..b2bccd3ca57f2e824af9207ffe5c618610f5300f 100644 --- a/src/com/android/documentsui/AbstractActionHandler.java +++ b/src/com/android/documentsui/AbstractActionHandler.java @@ -22,8 +22,10 @@ import static com.android.documentsui.base.Shared.DEBUG; import android.app.Activity; import android.app.LoaderManager.LoaderCallbacks; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.content.IntentSender; import android.content.Loader; import android.content.pm.ResolveInfo; import android.database.Cursor; @@ -76,6 +78,10 @@ import javax.annotation.Nullable; public abstract class AbstractActionHandler implements ActionHandler { + @VisibleForTesting + public static final int CODE_FORWARD = 42; + public static final int CODE_AUTHENTICATION = 43; + @VisibleForTesting static final int LOADER_ID = 42; @@ -147,6 +153,32 @@ public abstract class AbstractActionHandler listener).executeOnExecutor(ProviderExecutor.forAuthority(root.authority)); } + @Override + public void startAuthentication(PendingIntent intent) { + try { + mActivity.startIntentSenderForResult(intent.getIntentSender(), CODE_AUTHENTICATION, + null, 0, 0, 0); + } catch (IntentSender.SendIntentException cancelled) { + Log.d(TAG, "Authentication Pending Intent either canceled or ignored."); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case CODE_AUTHENTICATION: + onAuthenticationResult(resultCode); + break; + } + } + + private void onAuthenticationResult(int resultCode) { + if (resultCode == Activity.RESULT_OK) { + Log.v(TAG, "Authentication was successful. Refreshing directory now."); + mActivity.refreshCurrentRootAndDirectory(AnimationView.ANIM_NONE); + } + } + @Override public void getRootDocument(RootInfo root, int timeout, Consumer callback) { GetRootDocumentTask task = new GetRootDocumentTask( diff --git a/src/com/android/documentsui/ActionHandler.java b/src/com/android/documentsui/ActionHandler.java index cb666022674575ada6ebaae8bb50903f615174e1..e4abf7d0b9d3e5834a01fbcc2e33d931705479c1 100644 --- a/src/com/android/documentsui/ActionHandler.java +++ b/src/com/android/documentsui/ActionHandler.java @@ -17,6 +17,7 @@ package com.android.documentsui; import android.annotation.IntDef; +import android.app.PendingIntent; import android.content.ContentProvider; import android.content.Intent; import android.content.pm.ResolveInfo; @@ -48,6 +49,8 @@ public interface ActionHandler { public static final int VIEW_TYPE_REGULAR = 1; public static final int VIEW_TYPE_PREVIEW = 2; + void onActivityResult(int requestCode, int resultCode, Intent data); + void openSettings(RootInfo root); /** @@ -73,6 +76,13 @@ public interface ActionHandler { */ void refreshDocument(DocumentInfo doc, BooleanConsumer callback); + + /** + * Attempts to start the authentication process caused by + * {@link android.app.AuthenticationRequiredException}. + */ + void startAuthentication(PendingIntent intent); + void showAppDetails(ResolveInfo info); void openRoot(RootInfo root); diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java index 929bc48aca39d35a0dddcabc9f6114214fee31d9..c89079e3b29a6bc09bf90358eb90149c63bf9563 100644 --- a/src/com/android/documentsui/BaseActivity.java +++ b/src/com/android/documentsui/BaseActivity.java @@ -574,6 +574,11 @@ public abstract class BaseActivity return super.dispatchKeyEvent(event); } + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + mInjector.actions.onActivityResult(requestCode, resultCode, data); + } + /** * Pops the top entry off the directory stack, and returns the user to the previous directory. * If the directory stack only contains one item, this method does nothing. diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java index adc9255453531756c5bb2b709ec56e465749febc..e10cdbac353aaa334bd2f00e5b471eb4c2ca44d7 100644 --- a/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -1175,5 +1175,10 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On public void onBindDocumentHolder(DocumentHolder holder, Cursor cursor) { setupDragAndDropOnDocumentView(holder.itemView, cursor); } + + @Override + public ActionHandler getActionHandler() { + return mActions; + } } } diff --git a/src/com/android/documentsui/dirlist/DocumentsAdapter.java b/src/com/android/documentsui/dirlist/DocumentsAdapter.java index d1f0e4ab277397d113d335aac7db75096e5f0c6d..82f2524a7a182ad520e308818958267e3ba86b7b 100644 --- a/src/com/android/documentsui/dirlist/DocumentsAdapter.java +++ b/src/com/android/documentsui/dirlist/DocumentsAdapter.java @@ -24,6 +24,7 @@ import android.provider.DocumentsContract.Document; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; +import com.android.documentsui.ActionHandler; import com.android.documentsui.Model; import com.android.documentsui.base.EventListener; import com.android.documentsui.base.Features; @@ -105,6 +106,7 @@ public abstract class DocumentsAdapter interface Environment { Context getContext(); Features getFeatures(); + ActionHandler getActionHandler(); int getColumnCount(); State getDisplayState(); boolean isInSearchMode(); diff --git a/src/com/android/documentsui/dirlist/Message.java b/src/com/android/documentsui/dirlist/Message.java index b9c67c68f757efe64ca1c9e09635c5b5961daf62..c90b12efd1542376009ec18bfaea4169080ef653 100644 --- a/src/com/android/documentsui/dirlist/Message.java +++ b/src/com/android/documentsui/dirlist/Message.java @@ -129,11 +129,7 @@ abstract class Message { mCallback = () -> { AuthenticationRequiredException exception = (AuthenticationRequiredException) event.getException(); - try { - exception.getUserAction().send(); - } catch (PendingIntent.CanceledException ignored) { - Log.d(TAG, "User Action either caneled or ignored."); - } + mEnv.getActionHandler().startAuthentication(exception.getUserAction()); }; } } diff --git a/src/com/android/documentsui/picker/ActionHandler.java b/src/com/android/documentsui/picker/ActionHandler.java index b00becfe6c0d68365d03cc3397ee92cf9774da46..7e740b18ccebbb8558a6deae00ec9e19c1dd4c5b 100644 --- a/src/com/android/documentsui/picker/ActionHandler.java +++ b/src/com/android/documentsui/picker/ActionHandler.java @@ -26,6 +26,7 @@ import static com.android.documentsui.base.State.ACTION_PICK_COPY_DESTINATION; import android.app.Activity; import android.app.FragmentManager; import android.content.ClipData; +import android.content.ComponentName; import android.content.Intent; import android.content.pm.ResolveInfo; import android.net.Uri; @@ -189,6 +190,32 @@ class ActionHandler extends AbstractActionHandler> mInjector; private SharedInputHandler mSharedInputHandler; @@ -200,34 +195,6 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons { } } - @Override - public void onAppPicked(ResolveInfo info) { - final Intent intent = new Intent(getIntent()); - intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_FORWARD_RESULT); - intent.setComponent(new ComponentName( - info.activityInfo.applicationInfo.packageName, info.activityInfo.name)); - startActivityForResult(intent, CODE_FORWARD); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - if (DEBUG) Log.d(TAG, "onActivityResult() code=" + resultCode); - - // Only relay back results when not canceled; otherwise stick around to - // let the user pick another app/backend. - if (requestCode == CODE_FORWARD && resultCode != RESULT_CANCELED) { - - // Remember that we last picked via external app - mLastAccessed.setLastAccessedToExternalApp(this); - - // Pass back result to original caller - setResult(resultCode, data); - finish(); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); diff --git a/src/com/android/documentsui/services/ResolvedResourcesJob.java b/src/com/android/documentsui/services/ResolvedResourcesJob.java index 1632121e8cf419473032e0b5d9cb23233c30e22e..182a9ed35c28da03593e97929cfcdbf8bb4bef0a 100644 --- a/src/com/android/documentsui/services/ResolvedResourcesJob.java +++ b/src/com/android/documentsui/services/ResolvedResourcesJob.java @@ -16,7 +16,6 @@ package com.android.documentsui.services; -import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.net.Uri; diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 6a73f3939ac67d2a61f8073c39ae39f515c70386..0dc66a6714b58f979b1fda2a04185f019f8ddc43 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -17,6 +17,13 @@ + + + + + + + startActivity; public TestEventListener startService; + public TestEventListener> startIntentSender; public TestEventListener rootPicked; public TestEventListener refreshCurrentRootAndDirectory; public TestEventListener setRootsDrawerOpen; @@ -77,6 +81,7 @@ public abstract class TestActivity extends AbstractBase { startActivity = new TestEventListener<>(); startService = new TestEventListener<>(); + startIntentSender = new TestEventListener<>(); rootPicked = new TestEventListener<>(); refreshCurrentRootAndDirectory = new TestEventListener<>(); setRootsDrawerOpen = new TestEventListener<>(); @@ -125,6 +130,13 @@ public abstract class TestActivity extends AbstractBase { return packageMgr; } + @Override + public final void startIntentSenderForResult(IntentSender intent, int requestCode, + @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) + throws IntentSender.SendIntentException { + startIntentSender.accept(new Pair<>(intent, requestCode)); + } + @Override public final void onRootPicked(RootInfo root) { rootPicked.accept(root); diff --git a/tests/common/com/android/documentsui/testing/TestResolveInfo.java b/tests/common/com/android/documentsui/testing/TestResolveInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..89db0b6b16987a6ba35ebda90b7d652705efb761 --- /dev/null +++ b/tests/common/com/android/documentsui/testing/TestResolveInfo.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.documentsui.testing; + +import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; +import android.content.pm.ResolveInfo; + +public class TestResolveInfo { + + public static ResolveInfo create() { + ResolveInfo info = new ResolveInfo(); + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.name = "DocsUiTestActivity"; + ApplicationInfo appInfo = new ApplicationInfo(); + appInfo.packageName = "com.documentsui.test"; + activityInfo.applicationInfo = appInfo; + info.activityInfo = activityInfo; + return info; + } +} diff --git a/tests/unit/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java b/tests/unit/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java index b61fea154edcf28f4f8143ee52a4c0cb15c912f5..4ce5e21d890a806534de8e670d71a16444786568 100644 --- a/tests/unit/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java +++ b/tests/unit/com/android/documentsui/dirlist/DirectoryAddonsAdapterTest.java @@ -25,9 +25,11 @@ import android.support.v7.widget.RecyclerView; import android.test.AndroidTestCase; import android.view.ViewGroup; +import com.android.documentsui.ActionHandler; import com.android.documentsui.Model; import com.android.documentsui.base.Features; import com.android.documentsui.base.State; +import com.android.documentsui.testing.TestActionHandler; import com.android.documentsui.testing.TestEnv; @MediumTest @@ -37,10 +39,12 @@ public class DirectoryAddonsAdapterTest extends AndroidTestCase { private TestEnv mEnv; private DirectoryAddonsAdapter mAdapter; + private ActionHandler mActionHandler; public void setUp() { mEnv = TestEnv.create(AUTHORITY); + mActionHandler = new TestActionHandler(); mEnv.clear(); final Context testContext = TestContext.createStorageTestContext(getContext(), AUTHORITY); @@ -136,6 +140,9 @@ public class DirectoryAddonsAdapterTest extends AndroidTestCase { return mEnv.features; } + @Override + public ActionHandler getActionHandler() { return mActionHandler; } + @Override public boolean isSelected(String id) { return false; diff --git a/tests/unit/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java b/tests/unit/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java index 03957325ee82da5729d66b0b5eb77897487c99ae..18c5ef945594e8b0a5b577d357e513dcc0a45cb9 100644 --- a/tests/unit/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java +++ b/tests/unit/com/android/documentsui/dirlist/ModelBackedDocumentsAdapterTest.java @@ -16,14 +16,17 @@ package com.android.documentsui.dirlist; +import android.app.PendingIntent; import android.content.Context; import android.database.Cursor; import android.support.test.filters.MediumTest; import android.test.AndroidTestCase; +import com.android.documentsui.ActionHandler; import com.android.documentsui.Model; import com.android.documentsui.base.Features; import com.android.documentsui.base.State; +import com.android.documentsui.testing.TestActionHandler; import com.android.documentsui.testing.TestEnv; @MediumTest @@ -32,12 +35,14 @@ public class ModelBackedDocumentsAdapterTest extends AndroidTestCase { private static final String AUTHORITY = "test_authority"; private TestEnv mEnv; + private ActionHandler mActionHandler; private ModelBackedDocumentsAdapter mAdapter; public void setUp() { final Context testContext = TestContext.createStorageTestContext(getContext(), AUTHORITY); mEnv = TestEnv.create(AUTHORITY); + mActionHandler = new TestActionHandler(); DocumentsAdapter.Environment env = new TestEnvironment(testContext); @@ -59,6 +64,9 @@ public class ModelBackedDocumentsAdapterTest extends AndroidTestCase { return mEnv.features; } + @Override + public ActionHandler getActionHandler() { return mActionHandler; } + private TestEnvironment(Context testContext) { this.testContext = testContext; } diff --git a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java index 8752311f8ff0f8605c2daaa0740aab0e944d6c06..24d30bca3aba065b6806c176762b121edec7511e 100644 --- a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java +++ b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java @@ -27,6 +27,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; +import android.app.Activity; +import android.app.PendingIntent; import android.content.ClipData; import android.content.Intent; import android.net.Uri; @@ -34,10 +36,12 @@ import android.os.Parcelable; import android.provider.DocumentsContract; import android.provider.DocumentsContract.Path; import android.support.test.filters.MediumTest; +import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.util.Pair; import android.view.DragEvent; +import com.android.documentsui.AbstractActionHandler; import com.android.documentsui.R; import com.android.documentsui.TestActionModeAddons; import com.android.documentsui.archives.ArchivesProvider; @@ -483,6 +487,35 @@ public class ActionHandlerTest { } } + @Test + public void testAuthentication() throws Exception { + PendingIntent intent = PendingIntent.getActivity( + InstrumentationRegistry.getInstrumentation().getTargetContext(), 0, new Intent(), + 0); + + mHandler.startAuthentication(intent); + assertEquals(intent.getIntentSender(), mActivity.startIntentSender.getLastValue().first); + assertEquals(AbstractActionHandler.CODE_AUTHENTICATION, + mActivity.startIntentSender.getLastValue().second.intValue()); + } + + @Test + public void testOnActivityResult_onOK() throws Exception { + mHandler.onActivityResult(AbstractActionHandler.CODE_AUTHENTICATION, Activity.RESULT_OK, + null); + mActivity.refreshCurrentRootAndDirectory.assertCalled(); + } + + @Test + public void testOnActivityResult_onNotOK() throws Exception { + mHandler.onActivityResult(0, Activity.RESULT_OK, null); + mActivity.refreshCurrentRootAndDirectory.assertNotCalled(); + + mHandler.onActivityResult(AbstractActionHandler.CODE_AUTHENTICATION, + Activity.RESULT_CANCELED, null); + mActivity.refreshCurrentRootAndDirectory.assertNotCalled(); + } + private void assertRootPicked(Uri expectedUri) throws Exception { mEnv.beforeAsserts(); diff --git a/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java b/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java index 56b43743387bd7ee70483bc6456804ff5093036d..d559e6c1fed30d763dbd93e619e40907949ccb04 100644 --- a/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java +++ b/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java @@ -31,6 +31,7 @@ import android.provider.DocumentsContract.Path; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; +import com.android.documentsui.AbstractActionHandler; import com.android.documentsui.R; import com.android.documentsui.base.DocumentStack; import com.android.documentsui.base.RootInfo; @@ -41,6 +42,7 @@ import com.android.documentsui.testing.DocumentStackAsserts; import com.android.documentsui.testing.TestEnv; import com.android.documentsui.testing.TestProvidersAccess; import com.android.documentsui.testing.TestLastAccessedStorage; +import com.android.documentsui.testing.TestResolveInfo; import org.junit.AfterClass; import org.junit.Before; @@ -410,6 +412,38 @@ public class ActionHandlerTest { mActivity.finishedHandler.assertCalled(); } + @Test + public void testOnAppPickedResult_OnOK() throws Exception { + Intent intent = new Intent(); + mHandler.onActivityResult(AbstractActionHandler.CODE_FORWARD, Activity.RESULT_OK, intent); + mActivity.finishedHandler.assertCalled(); + mActivity.setResult.assertCalled(); + + assertEquals(Activity.RESULT_OK, (long) mActivity.setResult.getLastValue().first); + assertEquals(intent, mActivity.setResult.getLastValue().second); + } + + @Test + public void testOnAppPickedResult_OnNotOK() throws Exception { + Intent intent = new Intent(); + mHandler.onActivityResult(0, Activity.RESULT_OK, intent); + mActivity.finishedHandler.assertNotCalled(); + mActivity.setResult.assertNotCalled(); + + mHandler.onActivityResult(AbstractActionHandler.CODE_FORWARD, Activity.RESULT_CANCELED, + intent); + mActivity.finishedHandler.assertNotCalled(); + mActivity.setResult.assertNotCalled(); + } + + @Test + public void testOpenAppRoot() throws Exception { + mHandler.openRoot(TestResolveInfo.create()); + assertEquals((long) mActivity.startActivityForResult.getLastValue().second, + AbstractActionHandler.CODE_FORWARD); + assertNotNull(mActivity.startActivityForResult.getLastValue().first); + } + private void testInitLocationDefaultToRecentsOnAction(@ActionType int action) throws Exception { mEnv.state.action = action; diff --git a/tests/unit/com/android/documentsui/picker/TestActivity.java b/tests/unit/com/android/documentsui/picker/TestActivity.java index 8ce91b80e8421bc2bacaddf5cb490d6f3ac21c80..2a7b6be608185b0b111616f784092f3543b16fb5 100644 --- a/tests/unit/com/android/documentsui/picker/TestActivity.java +++ b/tests/unit/com/android/documentsui/picker/TestActivity.java @@ -16,6 +16,7 @@ package com.android.documentsui.picker; +import android.annotation.RequiresPermission; import android.content.Intent; import android.util.Pair; @@ -27,6 +28,7 @@ import org.mockito.Mockito; public abstract class TestActivity extends AbstractBase { public TestEventListener> setResult; + public TestEventListener> startActivityForResult; public static TestActivity create(TestEnv env) { TestActivity activity = Mockito.mock(TestActivity.class, Mockito.CALLS_REAL_METHODS); @@ -39,12 +41,18 @@ public abstract class TestActivity extends AbstractBase { super.init(env); setResult = new TestEventListener<>(); + startActivityForResult = new TestEventListener<>(); } @Override public void setResult(int resultCode, Intent intent, int notUsed) { setResult.accept(Pair.create(resultCode, intent)); } + + @Override + public final void startActivityForResult(@RequiresPermission Intent intent, int requestCode) { + startActivityForResult.accept(Pair.create(intent, requestCode)); + } } // Trick Mockito into finding our Addons methods correctly. W/o this