Still very basic, but will be adding as we find more issues pop up.
Test: Ran the tests
Change-Id: Ib9863509d140ff425ce446ecc876f53494d4fd0e
}
} else {
try {
- AppGlobals.getPackageManager().setLastChosenActivity(intent,
- intent.resolveType(getContentResolver()),
- PackageManager.MATCH_DEFAULT_ONLY,
- filter, bestMatch, intent.getComponent());
+ mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch);
} catch (RemoteException re) {
Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
}
protected boolean rebuildList() {
List<ResolvedComponentInfo> currentResolveList = null;
try {
- final Intent primaryIntent = getTargetIntent();
- mLastChosen = AppGlobals.getPackageManager().getLastChosenActivity(
- primaryIntent, primaryIntent.resolveTypeIfNeeded(getContentResolver()),
- PackageManager.MATCH_DEFAULT_ONLY);
+ mLastChosen = mResolverListController.getLastChosen();
} catch (RemoteException re) {
Log.d(TAG, "Error calling getLastChosenActivity\n" + re);
}
import android.annotation.WorkerThread;
import android.app.ActivityManager;
+import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.os.RemoteException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
}
@VisibleForTesting
+ ResolveInfo getLastChosen() throws RemoteException {
+ return AppGlobals.getPackageManager().getLastChosenActivity(
+ mTargetIntent, mTargetIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+ PackageManager.MATCH_DEFAULT_ONLY);
+ }
+
+ @VisibleForTesting
+ void setLastChosen(Intent intent, IntentFilter filter, int match)
+ throws RemoteException {
+ AppGlobals.getPackageManager().setLastChosenActivity(intent,
+ intent.resolveType(mContext.getContentResolver()),
+ PackageManager.MATCH_DEFAULT_ONLY,
+ filter, match, intent.getComponent());
+ }
+
+ @VisibleForTesting
public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent(
boolean shouldGetResolvedFilter,
boolean shouldGetActivityMetadata,
<activity android:name="android.app.EmptyActivity">
</activity>
<activity android:name="com.android.internal.app.ChooserWrapperActivity"/>
+ <activity android:name="com.android.internal.app.ResolverWrapperActivity"/>
<receiver android:name="android.app.activity.AbortReceiver">
<intent-filter android:priority="1">
private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
for (int i = 0; i < numberOfResults; i++) {
- infoList.add(ChooserDataProvider.createResolvedComponentInfo(i));
+ infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
}
return infoList;
}
import static org.mockito.Mockito.mock;
-
-/**
- * Simple wrapper around chooser activity to be able to initiate it under test
- */
public class ChooserWrapperActivity extends ChooserActivity {
+ /*
+ * Simple wrapper around chooser activity to be able to initiate it under test
+ */
static final OverrideData sOverrides = new OverrideData();
private UsageStatsManager mUsm;
resolverListController = mock(ResolverListController.class);
}
}
-}
\ No newline at end of file
+}
--- /dev/null
+/*
+ * Copyright (C) 2016 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.internal.app;
+
+import com.android.internal.R;
+import com.android.internal.app.ResolverActivity.ResolvedComponentInfo;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+
+import android.app.usage.UsageStats;
+import android.app.usage.UsageStatsManager;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static android.os.SystemClock.sleep;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * Resolver activity instrumentation tests
+ */
+@RunWith(AndroidJUnit4.class)
+public class ResolverActivityTest {
+ @Rule
+ public ActivityTestRule<ResolverWrapperActivity> mActivityRule =
+ new ActivityTestRule<>(ResolverWrapperActivity.class, false,
+ false);
+
+ @Before
+ public void cleanOverrideData() {
+ sOverrides.reset();
+ }
+
+ @Test
+ public void twoOptionsAndUserSelectsOne() throws InterruptedException {
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+
+ assertThat(activity.getAdapter().getCount(), is(2));
+
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+
+ ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+ onView(withText(toChoose.activityInfo.name))
+ .perform(click());
+ onView(withId(R.id.button_once))
+ .perform(click());
+ waitForIdle();
+ assertThat(chosen[0], is(toChoose));
+ }
+
+ @Test
+ public void hasLastChosenActivity() throws Exception {
+ Intent sendIntent = createSendImageIntent();
+ List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(2);
+
+ when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+ Mockito.anyBoolean(),
+ Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
+ when(sOverrides.resolverListController.getLastChosen())
+ .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0));
+
+ final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+
+ // The other entry is filtered to the last used slot
+ assertThat(activity.getAdapter().getCount(), is(1));
+
+ ResolveInfo[] chosen = new ResolveInfo[1];
+ sOverrides.onSafelyStartCallback = targetInfo -> {
+ chosen[0] = targetInfo.getResolveInfo();
+ return true;
+ };
+
+ ResolveInfo toChoose = resolvedComponentInfos.get(0).getResolveInfoAt(0);
+ onView(withId(R.id.title)).perform(click());
+ onView(withId(R.id.button_once))
+ .perform(click());
+ waitForIdle();
+ assertThat(chosen[0], is(toChoose));
+ }
+
+ private Intent createSendImageIntent() {
+ Intent sendIntent = new Intent();
+ sendIntent.setAction(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_TEXT, "testing intent sending");
+ sendIntent.setType("image/jpeg");
+ return sendIntent;
+ }
+
+ private List<ResolvedComponentInfo> createResolvedComponentsForTest(int numberOfResults) {
+ List<ResolvedComponentInfo> infoList = new ArrayList<>(numberOfResults);
+ for (int i = 0; i < numberOfResults; i++) {
+ infoList.add(ResolverDataProvider.createResolvedComponentInfo(i));
+ }
+ return infoList;
+ }
+
+ private void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+}
\ No newline at end of file
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.os.UserHandle;
-import android.service.chooser.ChooserTarget;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
/**
- * Utility class used by chooser tests to create mock data
+ * Utility class used by resolver tests to create mock data
*/
-class ChooserDataProvider {
+class ResolverDataProvider {
static ResolverActivity.ResolvedComponentInfo createResolvedComponentInfo(int i) {
return new ResolverActivity.ResolvedComponentInfo(createComponentName(i),
--- /dev/null
+/*
+ * 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.internal.app;
+
+import android.app.usage.UsageStatsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import java.util.function.Function;
+
+import static org.mockito.Mockito.mock;
+
+
+/*
+ * Simple wrapper around chooser activity to be able to initiate it under test
+ */
+public class ResolverWrapperActivity extends ResolverActivity {
+ static final OverrideData sOverrides = new OverrideData();
+ private UsageStatsManager mUsm;
+
+ ResolveListAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public boolean isVoiceInteraction() {
+ if (sOverrides.isVoiceInteraction != null) {
+ return sOverrides.isVoiceInteraction;
+ }
+ return super.isVoiceInteraction();
+ }
+
+ @Override
+ public void safelyStartActivity(TargetInfo cti) {
+ if (sOverrides.onSafelyStartCallback != null &&
+ sOverrides.onSafelyStartCallback.apply(cti)) {
+ return;
+ }
+ super.safelyStartActivity(cti);
+ }
+
+ @Override
+ protected ResolverListController createListController() {
+ return sOverrides.resolverListController;
+ }
+
+ @Override
+ public PackageManager getPackageManager() {
+ if (sOverrides.createPackageManager != null) {
+ return sOverrides.createPackageManager.apply(super.getPackageManager());
+ }
+ return super.getPackageManager();
+ }
+
+ /**
+ * We cannot directly mock the activity created since instrumentation creates it.
+ * <p>
+ * Instead, we use static instances of this object to modify behavior.
+ */
+ static class OverrideData {
+ @SuppressWarnings("Since15")
+ public Function<PackageManager, PackageManager> createPackageManager;
+ public Function<TargetInfo, Boolean> onSafelyStartCallback;
+ public ResolverListController resolverListController;
+ public Boolean isVoiceInteraction;
+
+ public void reset() {
+ onSafelyStartCallback = null;
+ isVoiceInteraction = null;
+ createPackageManager = null;
+ resolverListController = mock(ResolverListController.class);
+ }
+ }
+}
\ No newline at end of file