summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorMarcus Hagerott <mhagerott@google.com>2016-09-14 08:34:29 -0700
committerMarcus Hagerott <mhagerott@google.com>2016-09-20 14:48:17 -0700
commitc5083f9a9cf22f3b4acc2f8a6f098a37c6759800 (patch)
treec5ce011a67e3f24d2a381c81a23d3f1f5e9fd0bd /tests
parentd5cbb9eff63386f884645fba40fed34dd03bd070 (diff)
downloadpackages_apps_Contacts-c5083f9a9cf22f3b4acc2f8a6f098a37c6759800.tar.gz
packages_apps_Contacts-c5083f9a9cf22f3b4acc2f8a6f098a37c6759800.tar.bz2
packages_apps_Contacts-c5083f9a9cf22f3b4acc2f8a6f098a37c6759800.zip
Add dynamic launcher shortcuts.
Currently the shortcuts are created for the top 3 contacts returned from Contacts.CONTENT_STREQUENT_URI Test: Added unit tests for DynamicShortcuts but currently suppressed because they require AndroidJUnitRunner Manual: * Use N_MR1 device with recent dogfood Nexus launcher installed. * launch app * star some contacts if needed * press home * long press launcher icon * verify that starred contacts show in list of shortcuts * unstar some contacts * verify that shortcuts change * pin a shortcut * remove contact for pinned shortcut * verify that pinned shortcut is disabled * pin a shortcut * change name of contact for pinned shortcut * verify that name on pinned shortcut changes Also prevent disambiguation dialog for other home screen shortcuts Bug 30189449 Bug 31628994 Change-Id: Iace4b1c88b51ba1f7973c6f4ef90002fb92d0784
Diffstat (limited to 'tests')
-rw-r--r--tests/Android.mk5
-rw-r--r--tests/src/com/android/contacts/DynamicShortcutsTests.java307
-rw-r--r--tests/src/com/android/contacts/common/test/mocks/MockContentProvider.java44
3 files changed, 350 insertions, 6 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
index 48a00f42a..1176687d3 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -17,8 +17,9 @@ LOCAL_INSTRUMENTATION_FOR := Contacts
LOCAL_SDK_VERSION := current
LOCAL_MIN_SDK_VERSION := 21
-LOCAL_STATIC_JAVA_LIBRARIES := \
- mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES += \
+ hamcrest-library \
+ mockito-target-minus-junit4
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
diff --git a/tests/src/com/android/contacts/DynamicShortcutsTests.java b/tests/src/com/android/contacts/DynamicShortcutsTests.java
new file mode 100644
index 000000000..168b6465c
--- /dev/null
+++ b/tests/src/com/android/contacts/DynamicShortcutsTests.java
@@ -0,0 +1,307 @@
+/*
+ * 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.contacts;
+
+import android.annotation.TargetApi;
+import android.app.job.JobScheduler;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.support.test.filters.SdkSuppress;
+import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+
+import com.android.contacts.common.test.mocks.MockContentProvider;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.Matchers;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@TargetApi(Build.VERSION_CODES.N_MR1)
+@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N_MR1)
+// TODO: need to switch to android.support.test.runner.AndroidJUnitRunner for the @SdkSuppress
+// annotation to be respected. So for now we suppress this test to keep it from failing when run
+// by the build system.
+@Suppress
+@SmallTest
+public class DynamicShortcutsTests extends AndroidTestCase {
+
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ // Clean up the job if it was scheduled by these tests.
+ final JobScheduler scheduler = (JobScheduler) getContext()
+ .getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ scheduler.cancel(ContactsJobService.DYNAMIC_SHORTCUTS_JOB_ID);
+ }
+
+ // Basic smoke test to make sure the queries executed by DynamicShortcuts are valid as well
+ // as the integration with ShortcutManager. Note that this may change the state of the shortcuts
+ // on the device it is executed on.
+ public void test_refresh_doesntCrash() {
+ final DynamicShortcuts sut = new DynamicShortcuts(getContext());
+ sut.refresh();
+ // Pass because it didn't throw an exception.
+ }
+
+ public void test_createShortcutFromRow_hasCorrectResult() {
+ final DynamicShortcuts sut = createDynamicShortcuts();
+
+ final Cursor row = queryResult(
+ // ID, LOOKUP_KEY, DISPLAY_NAME_PRIMARY
+ 1l, "lookup_key", "John Smith"
+ );
+
+ row.moveToFirst();
+ final ShortcutInfo shortcut = sut.builderForContactShortcut(row).build();
+
+ assertEquals("lookup_key", shortcut.getId());
+ assertEquals(Contacts.getLookupUri(1, "lookup_key"), shortcut.getIntent().getData());
+ assertEquals(ContactsContract.QuickContact.ACTION_QUICK_CONTACT,
+ shortcut.getIntent().getAction());
+ assertEquals("John Smith", shortcut.getShortLabel());
+ assertEquals("John Smith", shortcut.getLongLabel());
+ assertEquals(1l, shortcut.getExtras().getLong(Contacts._ID));
+ }
+
+ public void test_builderForContactShortcut_ellipsizesLongNamesForLabels() {
+ final DynamicShortcuts sut = createDynamicShortcuts();
+ sut.setShortLabelMaxLength(5);
+ sut.setLongLabelMaxLength(10);
+
+ final ShortcutInfo shortcut = sut.builderForContactShortcut(1l, "lookup_key",
+ "123456789 1011").build();
+
+ assertEquals("1234…", shortcut.getShortLabel());
+ assertEquals("123456789…", shortcut.getLongLabel());
+ }
+
+ public void test_updatePinned_disablesShortcutsForRemovedContacts() {
+ final ShortcutManager mockShortcutManager = mock(ShortcutManager.class);
+ when(mockShortcutManager.getPinnedShortcuts()).thenReturn(
+ Collections.singletonList(shortcutFor(1l, "key1", "name1")));
+
+ final DynamicShortcuts sut = new DynamicShortcuts(getContext(), emptyResolver(),
+ mockShortcutManager);
+
+ sut.updatePinned();
+
+ verify(mockShortcutManager).disableShortcuts(
+ eq(Collections.singletonList("key1")), anyString());
+ }
+
+ public void test_updatePinned_updatesExistingShortcutsWithMatchingKeys() {
+ final ShortcutManager mockShortcutManager = mock(ShortcutManager.class);
+ when(mockShortcutManager.getPinnedShortcuts()).thenReturn(
+ Arrays.asList(
+ shortcutFor(1l, "key1", "name1"),
+ shortcutFor(2l, "key2", "name2"),
+ shortcutFor(3l, "key3", "name3")
+ ));
+
+ final DynamicShortcuts sut = createDynamicShortcuts(resolverWithExpectedQueries(
+ queryForSingleRow(Contacts.getLookupUri(1l, "key1"), 11l, "key1", "New Name1"),
+ queryForSingleRow(Contacts.getLookupUri(2l, "key2"), 2l, "key2", "name2"),
+ queryForSingleRow(Contacts.getLookupUri(3l, "key3"), 33l, "key3", "name3")
+ ), mockShortcutManager);
+
+ sut.updatePinned();
+
+ final ArgumentCaptor<List<ShortcutInfo>> updateArgs =
+ ArgumentCaptor.forClass((Class) List.class);
+
+ verify(mockShortcutManager).disableShortcuts(
+ eq(Collections.<String>emptyList()), anyString());
+ verify(mockShortcutManager).updateShortcuts(updateArgs.capture());
+
+ final List<ShortcutInfo> arg = updateArgs.getValue();
+ assertThat(arg.size(), equalTo(3));
+ assertThat(arg.get(0),
+ isShortcutForContact(11l, "key1", "New Name1"));
+ assertThat(arg.get(1),
+ isShortcutForContact(2l, "key2", "name2"));
+ assertThat(arg.get(2),
+ isShortcutForContact(33l, "key3", "name3"));
+ }
+
+ public void test_refresh_setsDynamicShortcutsToStrequentContacts() {
+ final ShortcutManager mockShortcutManager = mock(ShortcutManager.class);
+ when(mockShortcutManager.getPinnedShortcuts()).thenReturn(
+ Collections.<ShortcutInfo>emptyList());
+ final DynamicShortcuts sut = createDynamicShortcuts(resolverWithExpectedQueries(
+ queryFor(Contacts.CONTENT_STREQUENT_URI,
+ 1l, "starred_key", "starred name",
+ 2l, "freq_key", "freq name",
+ 3l, "starred_2", "Starred Two")), mockShortcutManager);
+
+ sut.refresh();
+
+ final ArgumentCaptor<List<ShortcutInfo>> updateArgs =
+ ArgumentCaptor.forClass((Class) List.class);
+
+ verify(mockShortcutManager).setDynamicShortcuts(updateArgs.capture());
+
+ final List<ShortcutInfo> arg = updateArgs.getValue();
+ assertThat(arg.size(), equalTo(3));
+ assertThat(arg.get(0), isShortcutForContact(1l, "starred_key", "starred name"));
+ assertThat(arg.get(1), isShortcutForContact(2l, "freq_key", "freq name"));
+ assertThat(arg.get(2), isShortcutForContact(3l, "starred_2", "Starred Two"));
+ }
+
+ public void test_scheduleUpdateJob_schedulesJob() {
+ final DynamicShortcuts sut = createDynamicShortcuts();
+ sut.scheduleUpdateJob();
+ assertThat(DynamicShortcuts.isJobScheduled(getContext()), Matchers.is(true));
+ }
+
+ private Matcher<ShortcutInfo> isShortcutForContact(final long id,
+ final String lookupKey, final String name) {
+ return new BaseMatcher<ShortcutInfo>() {
+ @Override
+ public boolean matches(Object o) {
+ if (!(o instanceof ShortcutInfo)) return false;
+ final ShortcutInfo other = (ShortcutInfo)o;
+ return id == other.getExtras().getLong(Contacts._ID)
+ && lookupKey.equals(other.getId())
+ && name.equals(other.getLongLabel())
+ && name.equals(other.getShortLabel());
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Should be a shortcut for contact with _ID=" + id +
+ " lookup=" + lookupKey + " and display_name=" + name);
+ }
+ };
+ }
+
+ private ShortcutInfo shortcutFor(long contactId, String lookupKey, String name) {
+ return new DynamicShortcuts(getContext())
+ .builderForContactShortcut(contactId, lookupKey, name).build();
+ }
+
+ private ContentResolver emptyResolver() {
+ final MockContentProvider provider = new MockContentProvider();
+ provider.expect(MockContentProvider.Query.forAnyUri())
+ .withAnyProjection()
+ .withAnySelection()
+ .withAnySortOrder()
+ .returnEmptyCursor();
+ return resolverWithContactsProvider(provider);
+ }
+
+ private MockContentProvider.Query queryFor(Uri uri, Object... rows) {
+ final MockContentProvider.Query query = MockContentProvider.Query
+ .forUrisMatching(uri.getAuthority(), uri.getPath())
+ .withProjection(DynamicShortcuts.PROJECTION)
+ .withAnySelection()
+ .withAnySortOrder();
+
+ populateQueryRows(query, DynamicShortcuts.PROJECTION.length, rows);
+ return query;
+ }
+
+ private MockContentProvider.Query queryForSingleRow(Uri uri, Object... row) {
+ return new MockContentProvider.Query(uri)
+ .withProjection(DynamicShortcuts.PROJECTION)
+ .withAnySelection()
+ .withAnySortOrder()
+ .returnRow(row);
+ }
+
+ private ContentResolver resolverWithExpectedQueries(MockContentProvider.Query... queries) {
+ final MockContentProvider provider = new MockContentProvider();
+ for (MockContentProvider.Query query : queries) {
+ provider.expect(query);
+ }
+ return resolverWithContactsProvider(provider);
+ }
+
+ private ContentResolver resolverWithContactsProvider(ContentProvider provider) {
+ final MockContentResolver resolver = new MockContentResolver();
+ resolver.addProvider(ContactsContract.AUTHORITY, provider);
+ return resolver;
+ }
+
+ private DynamicShortcuts createDynamicShortcuts() {
+ return createDynamicShortcuts(emptyResolver(), mock(ShortcutManager.class));
+ }
+
+ private DynamicShortcuts createDynamicShortcuts(ContentResolver resolver,
+ ShortcutManager shortcutManager) {
+ final DynamicShortcuts result = new DynamicShortcuts(getContext(), resolver,
+ shortcutManager);
+ // Use very long label limits to make checking shortcuts easier to understand
+ result.setShortLabelMaxLength(100);
+ result.setLongLabelMaxLength(100);
+ return result;
+ }
+
+ private void populateQueryRows(MockContentProvider.Query query, int numColumns,
+ Object... rows) {
+ for (int i = 0; i < rows.length; i += numColumns) {
+ Object[] row = new Object[numColumns];
+ for (int j = 0; j < numColumns; j++) {
+ row[j] = rows[i + j];
+ }
+ query.returnRow(row);
+ }
+ }
+
+ private Cursor queryResult(Object... values) {
+ return queryResult(DynamicShortcuts.PROJECTION, values);
+ }
+
+ private Cursor queryResult(String[] columns, Object... values) {
+ MatrixCursor result = new MatrixCursor(new String[] {
+ Contacts._ID, Contacts.LOOKUP_KEY,
+ Contacts.DISPLAY_NAME_PRIMARY
+ });
+ for (int i = 0; i < values.length; i += columns.length) {
+ MatrixCursor.RowBuilder builder = result.newRow();
+ for (int j = 0; j < columns.length; j++) {
+ builder.add(values[i + j]);
+ }
+ }
+ return result;
+ }
+}
diff --git a/tests/src/com/android/contacts/common/test/mocks/MockContentProvider.java b/tests/src/com/android/contacts/common/test/mocks/MockContentProvider.java
index 335e8d2a9..336467da7 100644
--- a/tests/src/com/android/contacts/common/test/mocks/MockContentProvider.java
+++ b/tests/src/com/android/contacts/common/test/mocks/MockContentProvider.java
@@ -20,6 +20,7 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import android.content.ContentValues;
+import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
@@ -43,6 +44,8 @@ public class MockContentProvider extends android.test.mock.MockContentProvider {
public static class Query {
private final Uri mUri;
+ private UriMatcher mMatcher;
+
private String[] mProjection;
private String[] mDefaultProjection;
private String mSelection;
@@ -56,6 +59,15 @@ public class MockContentProvider extends android.test.mock.MockContentProvider {
private boolean mExecuted;
+ private Query() {
+ mUri = null;
+ }
+
+ private Query(UriMatcher matcher) {
+ mUri = null;
+ mMatcher = matcher;
+ }
+
public Query(Uri uri) {
mUri = uri;
}
@@ -123,7 +135,11 @@ public class MockContentProvider extends android.test.mock.MockContentProvider {
public boolean equals(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
- if (!uri.equals(mUri)) {
+ if (mUri == null) {
+ if (mMatcher != null && mMatcher.match(uri) == UriMatcher.NO_MATCH) {
+ return false;
+ }
+ } else if (!uri.equals(mUri)) {
return false;
}
@@ -169,6 +185,23 @@ public class MockContentProvider extends android.test.mock.MockContentProvider {
}
return cursor;
}
+
+ public static Query forAnyUri() {
+ return new Query();
+ }
+
+ public static Query forUrisMatching(UriMatcher matcher) {
+ return new Query(matcher);
+ }
+
+ public static Query forUrisMatching(String authority, String... paths) {
+ final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
+ for (int i = 0; i < paths.length; i++) {
+ matcher.addURI(authority, paths[i], i);
+ }
+ return new Query(matcher);
+ }
+
}
public static class TypeQuery {
@@ -443,12 +476,15 @@ public class MockContentProvider extends android.test.mock.MockContentProvider {
return true;
}
- public Query expectQuery(Uri contentUri) {
- Query query = new Query(contentUri);
+ public Query expect(Query query) {
mExpectedQueries.add(query);
return query;
}
+ public Query expectQuery(Uri contentUri) {
+ return expect(new Query(contentUri));
+ }
+
public void expectTypeQuery(Uri uri, String type) {
mExpectedTypeQueries.put(uri, type);
}
@@ -598,7 +634,7 @@ public class MockContentProvider extends android.test.mock.MockContentProvider {
private static String queryToString(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
StringBuilder sb = new StringBuilder();
- sb.append(uri).append(" ");
+ sb.append(uri == null ? "<Any Uri>" : uri).append(" ");
if (projection != null) {
sb.append(Arrays.toString(projection));
} else {