summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBen Gilad <gilad@google.com>2013-08-21 09:46:00 -0700
committerAlon Albert <aalbert@google.com>2013-08-27 13:32:02 -0700
commitad582fa758c6a276c26c5cc52079613a653c81bf (patch)
tree75a42cbaaff649a4bc7dabe782e32dbf8164ebed /src
parente31b07390729cced594404410efb4448f94a95cb (diff)
downloadandroid_packages_apps_ContactsCommon-ad582fa758c6a276c26c5cc52079613a653c81bf.tar.gz
android_packages_apps_ContactsCommon-ad582fa758c6a276c26c5cc52079613a653c81bf.tar.bz2
android_packages_apps_ContactsCommon-ad582fa758c6a276c26c5cc52079613a653c81bf.zip
Add Support For Extended Directories
Change-Id: I5097f9d45ce39aad93ede3a01f43e1c58e36c9f1
Diffstat (limited to 'src')
-rw-r--r--src/com/android/contacts/common/extensions/ExtendedPhoneDirectoriesManager.java26
-rw-r--r--src/com/android/contacts/common/extensions/ExtensionsFactory.java89
-rw-r--r--src/com/android/contacts/common/list/ContactEntryListAdapter.java27
-rw-r--r--src/com/android/contacts/common/list/DefaultContactListAdapter.java2
-rw-r--r--src/com/android/contacts/common/list/DirectoryPartition.java16
-rw-r--r--src/com/android/contacts/common/list/PhoneNumberListAdapter.java179
6 files changed, 290 insertions, 49 deletions
diff --git a/src/com/android/contacts/common/extensions/ExtendedPhoneDirectoriesManager.java b/src/com/android/contacts/common/extensions/ExtendedPhoneDirectoriesManager.java
new file mode 100644
index 00000000..eb259343
--- /dev/null
+++ b/src/com/android/contacts/common/extensions/ExtendedPhoneDirectoriesManager.java
@@ -0,0 +1,26 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+
+package com.android.contacts.common.extensions;
+
+import android.content.Context;
+
+import com.android.contacts.common.list.DirectoryPartition;
+
+import java.util.List;
+
+/**
+ * An interface for adding extended phone directories to
+ * {@link com.android.contacts.common.list.PhoneNumberListAdapter}.
+ * An app that wishes to add custom phone directories should implement this class and advertise it
+ * in assets/contacts_extensions.properties. {@link ExtensionsFactory} will load the implementation
+ * and the extended directories will be added by
+ * {@link com.android.contacts.common.list.PhoneNumberListAdapter}.
+ */
+public interface ExtendedPhoneDirectoriesManager {
+
+ /**
+ * Return a list of extended directories to add. May return null if no directories are to be
+ * added.
+ */
+ List<DirectoryPartition> getExtendedDirectories(Context context);
+}
diff --git a/src/com/android/contacts/common/extensions/ExtensionsFactory.java b/src/com/android/contacts/common/extensions/ExtensionsFactory.java
new file mode 100644
index 00000000..d52429e6
--- /dev/null
+++ b/src/com/android/contacts/common/extensions/ExtensionsFactory.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2013 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.common.extensions;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
+
+/*
+ * A framework for adding extensions to Dialer. This class reads a property file from
+ * assets/contacts_extensions.properties and loads extension classes that an app has defined. If
+ * an extension class was not defined, null is returned.
+ */
+public class ExtensionsFactory {
+
+ private static String TAG = "ExtensionsFactory";
+
+ // Config filename for mappings of various class names to their custom
+ // implementations.
+ private static final String EXTENSIONS_PROPERTIES = "contacts_extensions.properties";
+
+ private static final String EXTENDED_PHONE_DIRECTORIES_KEY = "extendedPhoneDirectories";
+
+ private static Properties sProperties = null;
+ private static ExtendedPhoneDirectoriesManager mExtendedPhoneDirectoriesManager = null;
+
+ public static void init(Context context) {
+ if (sProperties != null) {
+ return;
+ }
+ try {
+ final InputStream fileStream = context.getAssets().open(EXTENSIONS_PROPERTIES);
+ sProperties = new Properties();
+ sProperties.load(fileStream);
+ fileStream.close();
+
+ final String className = sProperties.getProperty(EXTENDED_PHONE_DIRECTORIES_KEY);
+ if (className != null) {
+ mExtendedPhoneDirectoriesManager = createInstance(className);
+ } else {
+ Log.d(TAG, EXTENDED_PHONE_DIRECTORIES_KEY + " not found in properties file.");
+ }
+
+ } catch (FileNotFoundException e) {
+ // No custom extensions. Ignore.
+ Log.d(TAG, "No custom extensions.");
+ } catch (IOException e) {
+ Log.d(TAG, e.toString());
+ }
+ }
+
+ private static <T> T createInstance(String className) {
+ try {
+ Class<?> c = Class.forName(className);
+ //noinspection unchecked
+ return (T) c.newInstance();
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, className + ": unable to create instance.", e);
+ } catch (IllegalAccessException e) {
+ Log.e(TAG, className + ": unable to create instance.", e);
+ } catch (InstantiationException e) {
+ Log.e(TAG, className + ": unable to create instance.", e);
+ }
+ return null;
+ }
+
+ public static ExtendedPhoneDirectoriesManager getExtendedPhoneDirectoriesManager() {
+ return mExtendedPhoneDirectoriesManager;
+ }
+}
diff --git a/src/com/android/contacts/common/list/ContactEntryListAdapter.java b/src/com/android/contacts/common/list/ContactEntryListAdapter.java
index fb9c73a8..c9e74e43 100644
--- a/src/com/android/contacts/common/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/common/list/ContactEntryListAdapter.java
@@ -159,7 +159,7 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter {
}
}
- private int getPartitionByDirectoryId(long id) {
+ protected int getPartitionByDirectoryId(long id) {
int count = getPartitionCount();
for (int i = 0; i < count; i++) {
Partition partition = getPartition(i);
@@ -172,6 +172,20 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter {
return -1;
}
+ protected DirectoryPartition getDirectoryById(long id) {
+ int count = getPartitionCount();
+ for (int i = 0; i < count; i++) {
+ Partition partition = getPartition(i);
+ if (partition instanceof DirectoryPartition) {
+ final DirectoryPartition directoryPartition = (DirectoryPartition) partition;
+ if (directoryPartition.getDirectoryId() == id) {
+ return directoryPartition;
+ }
+ }
+ }
+ return null;
+ }
+
public abstract String getContactDisplayName(int position);
public abstract void configureLoader(CursorLoader loader, long directoryId);
@@ -247,6 +261,11 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter {
return mDirectoryResultLimit;
}
+ public int getDirectoryResultLimit(DirectoryPartition directoryPartition) {
+ final int limit = directoryPartition.getResultLimit();
+ return limit == DirectoryPartition.RESULT_LIMIT_DEFAULT ? mDirectoryResultLimit : limit;
+ }
+
public void setDirectoryResultLimit(int limit) {
this.mDirectoryResultLimit = limit;
}
@@ -548,10 +567,10 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter {
countText.setText(R.string.search_results_searching);
} else {
int count = cursor == null ? 0 : cursor.getCount();
+ final int limit = getDirectoryResultLimit(directoryPartition);
if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE
- && count >= getDirectoryResultLimit()) {
- countText.setText(mContext.getString(
- R.string.foundTooManyContacts, getDirectoryResultLimit()));
+ && count >= limit) {
+ countText.setText(mContext.getString(R.string.foundTooManyContacts, limit));
} else {
countText.setText(getQuantityText(
count, R.string.listFoundAllContactsZero, R.plurals.searchFoundContacts));
diff --git a/src/com/android/contacts/common/list/DefaultContactListAdapter.java b/src/com/android/contacts/common/list/DefaultContactListAdapter.java
index 6ad9e8b0..fb974b4d 100644
--- a/src/com/android/contacts/common/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/common/list/DefaultContactListAdapter.java
@@ -78,7 +78,7 @@ public class DefaultContactListAdapter extends ContactListAdapter {
String.valueOf(directoryId));
if (directoryId != Directory.DEFAULT && directoryId != Directory.LOCAL_INVISIBLE) {
builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
- String.valueOf(getDirectoryResultLimit()));
+ String.valueOf(getDirectoryResultLimit(getDirectoryById(directoryId))));
}
builder.appendQueryParameter(SearchSnippetColumns.SNIPPET_ARGS_PARAM_KEY,
SNIPPET_ARGS);
diff --git a/src/com/android/contacts/common/list/DirectoryPartition.java b/src/com/android/contacts/common/list/DirectoryPartition.java
index 022d1e60..9a8d4cc9 100644
--- a/src/com/android/contacts/common/list/DirectoryPartition.java
+++ b/src/com/android/contacts/common/list/DirectoryPartition.java
@@ -28,12 +28,15 @@ public final class DirectoryPartition extends CompositeCursorAdapter.Partition {
public static final int STATUS_LOADING = 1;
public static final int STATUS_LOADED = 2;
+ public static final int RESULT_LIMIT_DEFAULT = -1;
+
private long mDirectoryId;
private String mDirectoryType;
private String mDisplayName;
private int mStatus;
private boolean mPriorityDirectory;
private boolean mPhotoSupported;
+ private int mResultLimit = RESULT_LIMIT_DEFAULT;
public DirectoryPartition(boolean showIfEmpty, boolean hasHeader) {
super(showIfEmpty, hasHeader);
@@ -106,4 +109,17 @@ public final class DirectoryPartition extends CompositeCursorAdapter.Partition {
public void setPhotoSupported(boolean flag) {
this.mPhotoSupported = flag;
}
+
+ /**
+ * Max number of results for this directory. Defaults to {@link #RESULT_LIMIT_DEFAULT} which
+ * implies using the adapter's
+ * {@link com.android.contacts.common.list.ContactListAdapter#getDirectoryResultLimit()}
+ */
+ public int getResultLimit() {
+ return mResultLimit;
+ }
+
+ public void setResultLimit(int resultLimit) {
+ mResultLimit = resultLimit;
+ }
}
diff --git a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
index e58e257e..43797cce 100644
--- a/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
+++ b/src/com/android/contacts/common/list/PhoneNumberListAdapter.java
@@ -29,13 +29,13 @@ import android.provider.ContactsContract.ContactCounts;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Directory;
-import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.android.contacts.common.R;
-import com.android.contacts.common.CallUtil;
+import com.android.contacts.common.extensions.ExtendedPhoneDirectoriesManager;
+import com.android.contacts.common.extensions.ExtensionsFactory;
import java.util.ArrayList;
import java.util.List;
@@ -49,8 +49,17 @@ import java.util.List;
* API instead of {@link Phone}.
*/
public class PhoneNumberListAdapter extends ContactEntryListAdapter {
+
private static final String TAG = PhoneNumberListAdapter.class.getSimpleName();
+ // A list of extended directories to add to the directories from the database
+ private final List<DirectoryPartition> mExtendedDirectories;
+
+ // Extended directories will have ID's that are higher than any of the id's from the database.
+ // Thi sis so that we can identify them and set them up properly. If no extended directories
+ // exist, this will be Long.MAX_VALUE
+ private long mFirstExtendedDirectoryId = Long.MAX_VALUE;
+
public static class PhoneQuery {
public static final String[] PROJECTION_PRIMARY = new String[] {
Phone._ID, // 0
@@ -97,6 +106,14 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter {
super(context);
setDefaultFilterHeaderText(R.string.list_filter_phones);
mUnknownNameText = context.getText(android.R.string.unknownName);
+
+ final ExtendedPhoneDirectoriesManager manager
+ = ExtensionsFactory.getExtendedPhoneDirectoriesManager();
+ if (manager != null) {
+ mExtendedDirectories = manager.getExtendedDirectories(mContext);
+ } else {
+ mExtendedDirectories = null;
+ }
}
protected CharSequence getUnknownNameText() {
@@ -110,56 +127,75 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter {
@Override
public void configureLoader(CursorLoader loader, long directoryId) {
- final boolean isRemoteDirectoryQuery = isRemoteDirectory(directoryId);
- final Builder builder;
- if (isSearchMode()) {
- final Uri baseUri;
- if (isRemoteDirectoryQuery) {
- baseUri = Phone.CONTENT_FILTER_URI;
- } else if (mUseCallableUri) {
- baseUri = Callable.CONTENT_FILTER_URI;
+ String query = getQueryString();
+ if (query == null) {
+ query = "";
+ }
+ if (isExtendedDirectory(directoryId)) {
+ final DirectoryPartition directory = getExtendedDirectoryFromId(directoryId);
+ final Builder builder = Uri.parse(directory.getDirectoryType()).buildUpon();
+ builder.appendPath(query);
+ builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(getDirectoryResultLimit(directory)));
+ loader.setUri(builder.build());
+ loader.setProjection(PhoneQuery.PROJECTION_PRIMARY);
+ } else {
+ final boolean isRemoteDirectoryQuery = isRemoteDirectory(directoryId);
+ final Builder builder;
+ if (isSearchMode()) {
+ final Uri baseUri;
+ if (isRemoteDirectoryQuery) {
+ baseUri = Phone.CONTENT_FILTER_URI;
+ } else if (mUseCallableUri) {
+ baseUri = Callable.CONTENT_FILTER_URI;
+ } else {
+ baseUri = Phone.CONTENT_FILTER_URI;
+ }
+ builder = baseUri.buildUpon();
+ builder.appendPath(query); // Builder will encode the query
+ builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(directoryId));
+ if (isRemoteDirectoryQuery) {
+ builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
+ String.valueOf(getDirectoryResultLimit(getDirectoryById(directoryId))));
+ }
} else {
- baseUri = Phone.CONTENT_FILTER_URI;
+ final Uri baseUri = mUseCallableUri ? Callable.CONTENT_URI : Phone.CONTENT_URI;
+ builder = baseUri.buildUpon().appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT));
+ if (isSectionHeaderDisplayEnabled()) {
+ builder.appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true");
+ }
+ applyFilter(loader, builder, directoryId, getFilter());
}
- builder = baseUri.buildUpon();
- final String query = getQueryString();
- if (TextUtils.isEmpty(query)) {
- builder.appendPath("");
+
+ // Remove duplicates when it is possible.
+ builder.appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true");
+ loader.setUri(builder.build());
+
+ // TODO a projection that includes the search snippet
+ if (getContactNameDisplayOrder() ==
+ ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+ loader.setProjection(PhoneQuery.PROJECTION_PRIMARY);
} else {
- builder.appendPath(query); // Builder will encode the query
- }
- builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(directoryId));
- if (isRemoteDirectoryQuery) {
- builder.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
- String.valueOf(getDirectoryResultLimit()));
+ loader.setProjection(PhoneQuery.PROJECTION_ALTERNATIVE);
}
- } else {
- final Uri baseUri = mUseCallableUri ? Callable.CONTENT_URI : Phone.CONTENT_URI;
- builder = baseUri.buildUpon().appendQueryParameter(
- ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT));
- if (isSectionHeaderDisplayEnabled()) {
- builder.appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true");
+
+ if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
+ loader.setSortOrder(Phone.SORT_KEY_PRIMARY);
+ } else {
+ loader.setSortOrder(Phone.SORT_KEY_ALTERNATIVE);
}
- applyFilter(loader, builder, directoryId, getFilter());
}
+ }
- // Remove duplicates when it is possible.
- builder.appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true");
- loader.setUri(builder.build());
-
- // TODO a projection that includes the search snippet
- if (getContactNameDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
- loader.setProjection(PhoneQuery.PROJECTION_PRIMARY);
- } else {
- loader.setProjection(PhoneQuery.PROJECTION_ALTERNATIVE);
- }
+ private boolean isExtendedDirectory(long directoryId) {
+ return directoryId >= mFirstExtendedDirectoryId;
+ }
- if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
- loader.setSortOrder(Phone.SORT_KEY_PRIMARY);
- } else {
- loader.setSortOrder(Phone.SORT_KEY_ALTERNATIVE);
- }
+ private DirectoryPartition getExtendedDirectoryFromId(long directoryId) {
+ final int directoryIndex = (int) (directoryId - mFirstExtendedDirectoryId);
+ return mExtendedDirectories.get(directoryIndex);
}
/**
@@ -368,4 +404,59 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter {
public boolean usesCallableUri() {
return mUseCallableUri;
}
+
+ /**
+ * Override base implementation to inject extended directories between local & remote
+ * directories. This is done in the following steps:
+ * 1. Call base implementation to add directories from the cursor.
+ * 2. Iterate all base directories and establish the following information:
+ * a. The highest directory id so that we can assign unused id's to the extended directories.
+ * b. The index of the last non-remote directory. This is where we will insert extended
+ * directories.
+ * 3. Iterate the extended directories and for each one, assign an ID and insert it in the
+ * proper location.
+ */
+ @Override
+ public void changeDirectories(Cursor cursor) {
+ super.changeDirectories(cursor);
+ if (getDirectorySearchMode() == DirectoryListLoader.SEARCH_MODE_NONE) {
+ return;
+ }
+ final int numExtendedDirectories = mExtendedDirectories.size();
+ if (getPartitionCount() == cursor.getCount() + numExtendedDirectories) {
+ // already added all directories;
+ return;
+ }
+ //
+ mFirstExtendedDirectoryId = Long.MAX_VALUE;
+ if (numExtendedDirectories > 0) {
+ // The Directory.LOCAL_INVISIBLE is not in the cursor but we can't reuse it's
+ // "special" ID.
+ long maxId = Directory.LOCAL_INVISIBLE;
+ int insertIndex = 0;
+ for (int i = 0, n = getPartitionCount(); i < n; i++) {
+ final DirectoryPartition partition = (DirectoryPartition) getPartition(i);
+ final long id = partition.getDirectoryId();
+ if (id > maxId) {
+ maxId = id;
+ }
+ if (!isRemoteDirectory(id)) {
+ // assuming remote directories come after local, we will end up with the index
+ // where we should insert extended directories. This also works if there are no
+ // remote directories at all.
+ insertIndex = i + 1;
+ }
+ }
+ // Extended directories ID's cannot collide with base directories
+ mFirstExtendedDirectoryId = maxId + 1;
+ for (int i = 0; i < numExtendedDirectories; i++) {
+ final long id = mFirstExtendedDirectoryId + i;
+ final DirectoryPartition directory = mExtendedDirectories.get(i);
+ if (getPartitionByDirectoryId(id) == -1) {
+ addPartition(insertIndex, directory);
+ directory.setDirectoryId(id);
+ }
+ }
+ }
+ }
}