Move AccountTypeManager to ContactsCommon.
Moving AccountTypeManager to ContactsCommon so it can be shared by Contacts
and Dialer.
Bug: 6993891
Change-Id: I9526ac7cda758290caecabfcaebe620238d8dd6b
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index 64d5531..d87a1a0 100644
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -47,7 +47,7 @@
import android.widget.Toast;
import com.android.contacts.common.database.ContactUpdateUtils;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.RawContactDelta;
import com.android.contacts.model.RawContactDeltaList;
import com.android.contacts.model.RawContactModifier;
diff --git a/src/com/android/contacts/ContactsApplication.java b/src/com/android/contacts/ContactsApplication.java
index ef07cf9..82b2a25 100644
--- a/src/com/android/contacts/ContactsApplication.java
+++ b/src/com/android/contacts/ContactsApplication.java
@@ -30,8 +30,8 @@
import android.util.Log;
import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.list.ContactListFilterController;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.list.ContactListFilterController;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.test.InjectedServices;
import com.android.contacts.common.util.Constants;
import com.google.common.annotations.VisibleForTesting;
diff --git a/src/com/android/contacts/ContactsUtils.java b/src/com/android/contacts/ContactsUtils.java
index 57976e5..b68109e 100644
--- a/src/com/android/contacts/ContactsUtils.java
+++ b/src/com/android/contacts/ContactsUtils.java
@@ -26,7 +26,7 @@
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.test.NeededForTesting;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import java.util.List;
diff --git a/src/com/android/contacts/SplitAggregateView.java b/src/com/android/contacts/SplitAggregateView.java
index fabd58b..6e38549 100644
--- a/src/com/android/contacts/SplitAggregateView.java
+++ b/src/com/android/contacts/SplitAggregateView.java
@@ -35,7 +35,7 @@
import android.widget.ListView;
import android.widget.TextView;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import java.util.ArrayList;
diff --git a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
index 9bd4f45..f90ae7f 100644
--- a/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
+++ b/src/com/android/contacts/activities/ConfirmAddDetailActivity.java
@@ -61,7 +61,7 @@
import com.android.contacts.R;
import com.android.contacts.editor.Editor;
import com.android.contacts.editor.ViewIdGenerator;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.RawContact;
import com.android.contacts.model.RawContactDelta;
import com.android.contacts.model.RawContactDelta.ValuesDelta;
diff --git a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
index 13c49e1..26a8eae 100644
--- a/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorAccountsChangedActivity.java
@@ -30,7 +30,7 @@
import com.android.contacts.R;
import com.android.contacts.editor.ContactEditorUtils;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.util.AccountsListAdapter;
import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index efdf7da..d12da85 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -36,7 +36,7 @@
import com.android.contacts.R;
import com.android.contacts.editor.ContactEditorFragment;
import com.android.contacts.editor.ContactEditorFragment.SaveMode;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.util.DialogManager;
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
index c2764c3..c24a42f 100644
--- a/src/com/android/contacts/activities/GroupDetailActivity.java
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -33,7 +33,7 @@
import com.android.contacts.R;
import com.android.contacts.group.GroupDetailDisplayUtils;
import com.android.contacts.group.GroupDetailFragment;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
public class GroupDetailActivity extends ContactsActivity {
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 019eacd..9ad21e8 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -66,7 +66,7 @@
import com.android.contacts.list.ContactBrowseListFragment;
import com.android.contacts.list.ContactEntryListFragment;
import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.list.ContactListFilterController;
+import com.android.contacts.common.list.ContactListFilterController;
import com.android.contacts.common.list.ContactTileAdapter.DisplayType;
import com.android.contacts.list.ContactTileFrequentFragment;
import com.android.contacts.list.ContactTileListFragment;
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index fa71e72..7701106 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -82,7 +82,7 @@
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.MoreContactUtils;
import com.android.contacts.editor.SelectAccountDialogFragment;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.Contact;
import com.android.contacts.model.RawContact;
import com.android.contacts.model.RawContactDelta;
diff --git a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
index 46e4dad..b578d28 100644
--- a/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailUpdatesFragment.java
@@ -31,7 +31,7 @@
import com.android.contacts.R;
import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener;
import com.android.contacts.detail.ContactDetailDisplayUtils.StreamPhotoTag;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.Contact;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.util.StreamItemEntry;
diff --git a/src/com/android/contacts/detail/PhotoSelectionHandler.java b/src/com/android/contacts/detail/PhotoSelectionHandler.java
index d544b30..ec76379 100644
--- a/src/com/android/contacts/detail/PhotoSelectionHandler.java
+++ b/src/com/android/contacts/detail/PhotoSelectionHandler.java
@@ -39,7 +39,7 @@
import com.android.contacts.R;
import com.android.contacts.editor.PhotoActionPopup;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.RawContactModifier;
import com.android.contacts.model.RawContactDelta;
import com.android.contacts.model.RawContactDelta.ValuesDelta;
diff --git a/src/com/android/contacts/detail/StreamItemAdapter.java b/src/com/android/contacts/detail/StreamItemAdapter.java
index 398ac69..5c827dc 100644
--- a/src/com/android/contacts/detail/StreamItemAdapter.java
+++ b/src/com/android/contacts/detail/StreamItemAdapter.java
@@ -23,7 +23,7 @@
import android.widget.BaseAdapter;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.util.StreamItemEntry;
import com.google.common.collect.Lists;
diff --git a/src/com/android/contacts/editor/AggregationSuggestionView.java b/src/com/android/contacts/editor/AggregationSuggestionView.java
index 19481fe..439b1df 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionView.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionView.java
@@ -28,7 +28,7 @@
import com.android.contacts.R;
import com.android.contacts.editor.AggregationSuggestionEngine.RawContact;
import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.google.common.collect.Lists;
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index b019542..149f821 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -70,7 +70,7 @@
import com.android.contacts.detail.PhotoSelectionHandler;
import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
import com.android.contacts.editor.Editor.EditorListener;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.Contact;
import com.android.contacts.model.ContactLoader;
import com.android.contacts.model.RawContact;
diff --git a/src/com/android/contacts/editor/ContactEditorUtils.java b/src/com/android/contacts/editor/ContactEditorUtils.java
index 735f8be..dfbcc6b 100644
--- a/src/com/android/contacts/editor/ContactEditorUtils.java
+++ b/src/com/android/contacts/editor/ContactEditorUtils.java
@@ -27,7 +27,7 @@
import android.util.Log;
import com.android.contacts.common.test.NeededForTesting;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.google.common.annotations.VisibleForTesting;
diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java
index f96b40d..32296c2 100644
--- a/src/com/android/contacts/group/GroupBrowseListAdapter.java
+++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java
@@ -30,7 +30,7 @@
import com.android.contacts.GroupListLoader;
import com.android.contacts.R;
import com.android.contacts.common.model.account.AccountType;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.google.common.base.Objects;
/**
diff --git a/src/com/android/contacts/group/GroupDetailDisplayUtils.java b/src/com/android/contacts/group/GroupDetailDisplayUtils.java
index 1cf7831..fb0b0c8 100644
--- a/src/com/android/contacts/group/GroupDetailDisplayUtils.java
+++ b/src/com/android/contacts/group/GroupDetailDisplayUtils.java
@@ -23,7 +23,7 @@
import android.widget.TextView;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
public class GroupDetailDisplayUtils {
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 3ab6bc8..69d5165 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -53,7 +53,7 @@
import com.android.contacts.common.list.ContactTileAdapter;
import com.android.contacts.common.list.ContactTileView;
import com.android.contacts.list.GroupMemberTileAdapter;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
/**
diff --git a/src/com/android/contacts/group/GroupEditorFragment.java b/src/com/android/contacts/group/GroupEditorFragment.java
index 20a3334..16b7d86 100644
--- a/src/com/android/contacts/group/GroupEditorFragment.java
+++ b/src/com/android/contacts/group/GroupEditorFragment.java
@@ -68,7 +68,7 @@
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.editor.SelectAccountDialogFragment;
import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
import com.android.contacts.util.ViewUtil;
import com.google.common.base.Objects;
diff --git a/src/com/android/contacts/interactions/ContactDeletionInteraction.java b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
index c4387f3..2880f77 100644
--- a/src/com/android/contacts/interactions/ContactDeletionInteraction.java
+++ b/src/com/android/contacts/interactions/ContactDeletionInteraction.java
@@ -35,7 +35,7 @@
import com.android.contacts.ContactSaveService;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
diff --git a/src/com/android/contacts/interactions/ImportExportDialogFragment.java b/src/com/android/contacts/interactions/ImportExportDialogFragment.java
index 3f409cb..5013f5a 100644
--- a/src/com/android/contacts/interactions/ImportExportDialogFragment.java
+++ b/src/com/android/contacts/interactions/ImportExportDialogFragment.java
@@ -39,7 +39,7 @@
import com.android.contacts.R;
import com.android.contacts.editor.SelectAccountDialogFragment;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.util.AccountSelectionUtil;
import com.android.contacts.util.AccountsListAdapter.AccountListFilter;
diff --git a/src/com/android/contacts/list/AccountFilterActivity.java b/src/com/android/contacts/list/AccountFilterActivity.java
index cc986ad..b6f9310 100644
--- a/src/com/android/contacts/list/AccountFilterActivity.java
+++ b/src/com/android/contacts/list/AccountFilterActivity.java
@@ -37,7 +37,7 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.google.common.collect.Lists;
diff --git a/src/com/android/contacts/list/ContactListFilterController.java b/src/com/android/contacts/list/ContactListFilterController.java
deleted file mode 100644
index 569dd5d..0000000
--- a/src/com/android/contacts/list/ContactListFilterController.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2010 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.list;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-
-import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.model.AccountTypeManager;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Manages {@link com.android.contacts.common.list.ContactListFilter}. All methods must be called from UI thread.
- */
-public abstract class ContactListFilterController {
-
- // singleton to cache the filter controller
- private static ContactListFilterControllerImpl sFilterController = null;
-
- public interface ContactListFilterListener {
- void onContactListFilterChanged();
- }
-
- public static ContactListFilterController getInstance(Context context) {
- // We may need to synchronize this in the future if background task will call this.
- if (sFilterController == null) {
- sFilterController = new ContactListFilterControllerImpl(context);
- }
- return sFilterController;
- }
-
- public abstract void addListener(ContactListFilterListener listener);
-
- public abstract void removeListener(ContactListFilterListener listener);
-
- /**
- * Return the currently-active filter.
- */
- public abstract ContactListFilter getFilter();
-
- /**
- * @param filter the filter
- * @param persistent True when the given filter should be saved soon. False when the filter
- * should not be saved. The latter case may happen when some Intent requires a certain type of
- * UI (e.g. single contact) temporarily.
- */
- public abstract void setContactListFilter(ContactListFilter filter, boolean persistent);
-
- public abstract void selectCustomFilter();
-
- /**
- * Checks if the current filter is valid and reset the filter if not. It may happen when
- * an account is removed while the filter points to the account with
- * {@link ContactListFilter#FILTER_TYPE_ACCOUNT} type, for example. It may also happen if
- * the current filter is {@link ContactListFilter#FILTER_TYPE_SINGLE_CONTACT}, in
- * which case, we should switch to the last saved filter in {@link SharedPreferences}.
- */
- public abstract void checkFilterValidity(boolean notifyListeners);
-}
-
-/**
- * Stores the {@link ContactListFilter} selected by the user and saves it to
- * {@link SharedPreferences} if necessary.
- */
-class ContactListFilterControllerImpl extends ContactListFilterController {
- private final Context mContext;
- private final List<ContactListFilterListener> mListeners =
- new ArrayList<ContactListFilterListener>();
- private ContactListFilter mFilter;
-
- public ContactListFilterControllerImpl(Context context) {
- mContext = context;
- mFilter = ContactListFilter.restoreDefaultPreferences(getSharedPreferences());
- checkFilterValidity(true /* notify listeners */);
- }
-
- @Override
- public void addListener(ContactListFilterListener listener) {
- mListeners.add(listener);
- }
-
- @Override
- public void removeListener(ContactListFilterListener listener) {
- mListeners.remove(listener);
- }
-
- @Override
- public ContactListFilter getFilter() {
- return mFilter;
- }
-
- private SharedPreferences getSharedPreferences() {
- return PreferenceManager.getDefaultSharedPreferences(mContext);
- }
-
- @Override
- public void setContactListFilter(ContactListFilter filter, boolean persistent) {
- setContactListFilter(filter, persistent, true);
- }
-
- private void setContactListFilter(ContactListFilter filter, boolean persistent,
- boolean notifyListeners) {
- if (!filter.equals(mFilter)) {
- mFilter = filter;
- if (persistent) {
- ContactListFilter.storeToPreferences(getSharedPreferences(), mFilter);
- }
- if (notifyListeners && !mListeners.isEmpty()) {
- notifyContactListFilterChanged();
- }
- }
- }
-
- @Override
- public void selectCustomFilter() {
- setContactListFilter(ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_CUSTOM), true);
- }
-
- private void notifyContactListFilterChanged() {
- for (ContactListFilterListener listener : mListeners) {
- listener.onContactListFilterChanged();
- }
- }
-
- @Override
- public void checkFilterValidity(boolean notifyListeners) {
- if (mFilter == null) {
- return;
- }
-
- switch (mFilter.filterType) {
- case ContactListFilter.FILTER_TYPE_SINGLE_CONTACT:
- setContactListFilter(
- ContactListFilter.restoreDefaultPreferences(getSharedPreferences()),
- false, notifyListeners);
- break;
- case ContactListFilter.FILTER_TYPE_ACCOUNT:
- if (!filterAccountExists()) {
- // The current account filter points to invalid account. Use "all" filter
- // instead.
- setContactListFilter(ContactListFilter.createFilterWithType(
- ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), true, notifyListeners);
- }
- }
- }
-
- /**
- * @return true if the Account for the current filter exists.
- */
- private boolean filterAccountExists() {
- final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(mContext);
- final AccountWithDataSet filterAccount = new AccountWithDataSet(
- mFilter.accountName, mFilter.accountType, mFilter.dataSet);
- return accountTypeManager.contains(filterAccount, false);
- }
-}
diff --git a/src/com/android/contacts/list/ContactListFilterView.java b/src/com/android/contacts/list/ContactListFilterView.java
index 8102f65..d905683 100644
--- a/src/com/android/contacts/list/ContactListFilterView.java
+++ b/src/com/android/contacts/list/ContactListFilterView.java
@@ -27,7 +27,7 @@
import com.android.contacts.R;
import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
/**
diff --git a/src/com/android/contacts/list/CustomContactListFilterActivity.java b/src/com/android/contacts/list/CustomContactListFilterActivity.java
index 532bbee..9750cf5 100644
--- a/src/com/android/contacts/list/CustomContactListFilterActivity.java
+++ b/src/com/android/contacts/list/CustomContactListFilterActivity.java
@@ -55,7 +55,7 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.RawContactDelta.ValuesDelta;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index c9c895b..424a7b8 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -34,6 +34,7 @@
import com.android.contacts.R;
import com.android.contacts.common.list.ContactListAdapter;
import com.android.contacts.common.list.ContactListFilter;
+import com.android.contacts.common.list.ContactListFilterController;
import com.android.contacts.common.list.DefaultContactListAdapter;
import com.android.contacts.common.list.ProfileAndContactsLoader;
import com.android.contacts.editor.ContactEditorFragment;
diff --git a/src/com/android/contacts/list/PhoneNumberPickerFragment.java b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
index c7e4fc2..464afa3 100644
--- a/src/com/android/contacts/list/PhoneNumberPickerFragment.java
+++ b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
@@ -30,6 +30,7 @@
import com.android.contacts.R;
import com.android.contacts.common.list.ContactEntryListAdapter;
import com.android.contacts.common.list.ContactListFilter;
+import com.android.contacts.common.list.ContactListFilterController;
import com.android.contacts.common.list.ContactListItemView;
import com.android.contacts.common.list.DirectoryListLoader;
import com.android.contacts.common.list.PhoneNumberListAdapter;
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
deleted file mode 100644
index f81179b..0000000
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ /dev/null
@@ -1,824 +0,0 @@
-/*
- * Copyright (C) 2009 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.model;
-
-import android.accounts.Account;
-import android.accounts.AccountManager;
-import android.accounts.AuthenticatorDescription;
-import android.accounts.OnAccountsUpdateListener;
-import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.IContentService;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SyncAdapterType;
-import android.content.SyncStatusObserver;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.provider.ContactsContract;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.TimingLogger;
-
-import com.android.contacts.common.MoreContactUtils;
-import com.android.contacts.common.model.account.AccountType;
-import com.android.contacts.common.model.account.AccountTypeWithDataSet;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.contacts.common.model.account.ExchangeAccountType;
-import com.android.contacts.common.model.account.ExternalAccountType;
-import com.android.contacts.common.model.account.FallbackAccountType;
-import com.android.contacts.common.model.account.GoogleAccountType;
-import com.android.contacts.common.model.dataitem.DataKind;
-import com.android.contacts.common.test.NeededForTesting;
-import com.android.contacts.common.util.Constants;
-import com.android.contacts.list.ContactListFilterController;
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Singleton holder for all parsed {@link AccountType} available on the
- * system, typically filled through {@link PackageManager} queries.
- */
-public abstract class AccountTypeManager {
- static final String TAG = "AccountTypeManager";
-
- private static final Object mInitializationLock = new Object();
- private static AccountTypeManager mAccountTypeManager;
-
- /**
- * Requests the singleton instance of {@link AccountTypeManager} with data bound from
- * the available authenticators. This method can safely be called from the UI thread.
- */
- public static AccountTypeManager getInstance(Context context) {
- synchronized (mInitializationLock) {
- if (mAccountTypeManager == null) {
- context = context.getApplicationContext();
- mAccountTypeManager = new AccountTypeManagerImpl(context);
- }
- }
- return mAccountTypeManager;
- }
-
- /**
- * Set the instance of account type manager. This is only for and should only be used by unit
- * tests. While having this method is not ideal, it's simpler than the alternative of
- * holding this as a service in the ContactsApplication context class.
- *
- * @param mockManager The mock AccountTypeManager.
- */
- @NeededForTesting
- public static void setInstanceForTest(AccountTypeManager mockManager) {
- synchronized (mInitializationLock) {
- mAccountTypeManager = mockManager;
- }
- }
-
- /**
- * Returns the list of all accounts (if contactWritableOnly is false) or just the list of
- * contact writable accounts (if contactWritableOnly is true).
- */
- // TODO: Consider splitting this into getContactWritableAccounts() and getAllAccounts()
- public abstract List<AccountWithDataSet> getAccounts(boolean contactWritableOnly);
-
- /**
- * Returns the list of accounts that are group writable.
- */
- public abstract List<AccountWithDataSet> getGroupWritableAccounts();
-
- public abstract AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet);
-
- public final AccountType getAccountType(String accountType, String dataSet) {
- return getAccountType(AccountTypeWithDataSet.get(accountType, dataSet));
- }
-
- public final AccountType getAccountTypeForAccount(AccountWithDataSet account) {
- return getAccountType(account.getAccountTypeWithDataSet());
- }
-
- /**
- * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s
- * which support the "invite" feature and have one or more account.
- *
- * This is a filtered down and more "usable" list compared to
- * {@link #getAllInvitableAccountTypes}, where usable is defined as:
- * (1) making sure that the app that contributed the account type is not disabled
- * (in order to avoid presenting the user with an option that does nothing), and
- * (2) that there is at least one raw contact with that account type in the database
- * (assuming that the user probably doesn't use that account type).
- *
- * Warning: Don't use on the UI thread because this can scan the database.
- */
- public abstract Map<AccountTypeWithDataSet, AccountType> getUsableInvitableAccountTypes();
-
- /**
- * Find the best {@link DataKind} matching the requested
- * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}.
- * If no direct match found, we try searching {@link FallbackAccountType}.
- */
- public DataKind getKindOrFallback(AccountType type, String mimeType) {
- return type == null ? null : type.getKindForMimetype(mimeType);
- }
-
- /**
- * Returns all registered {@link AccountType}s, including extension ones.
- *
- * @param contactWritableOnly if true, it only returns ones that support writing contacts.
- */
- public abstract List<AccountType> getAccountTypes(boolean contactWritableOnly);
-
- /**
- * @param contactWritableOnly if true, it only returns ones that support writing contacts.
- * @return true when this instance contains the given account.
- */
- public boolean contains(AccountWithDataSet account, boolean contactWritableOnly) {
- for (AccountWithDataSet account_2 : getAccounts(false)) {
- if (account.equals(account_2)) {
- return true;
- }
- }
- return false;
- }
-}
-
-class AccountTypeManagerImpl extends AccountTypeManager
- implements OnAccountsUpdateListener, SyncStatusObserver {
-
- private static final Map<AccountTypeWithDataSet, AccountType>
- EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP =
- Collections.unmodifiableMap(new HashMap<AccountTypeWithDataSet, AccountType>());
-
- /**
- * A sample contact URI used to test whether any activities will respond to an
- * invitable intent with the given URI as the intent data. This doesn't need to be
- * specific to a real contact because an app that intercepts the intent should probably do so
- * for all types of contact URIs.
- */
- private static final Uri SAMPLE_CONTACT_URI = ContactsContract.Contacts.getLookupUri(
- 1, "xxx");
-
- private Context mContext;
- private AccountManager mAccountManager;
-
- private AccountType mFallbackAccountType;
-
- private List<AccountWithDataSet> mAccounts = Lists.newArrayList();
- private List<AccountWithDataSet> mContactWritableAccounts = Lists.newArrayList();
- private List<AccountWithDataSet> mGroupWritableAccounts = Lists.newArrayList();
- private Map<AccountTypeWithDataSet, AccountType> mAccountTypesWithDataSets = Maps.newHashMap();
- private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes =
- EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP;
-
- private final InvitableAccountTypeCache mInvitableAccountTypeCache;
-
- /**
- * The boolean value is equal to true if the {@link InvitableAccountTypeCache} has been
- * initialized. False otherwise.
- */
- private final AtomicBoolean mInvitablesCacheIsInitialized = new AtomicBoolean(false);
-
- /**
- * The boolean value is equal to true if the {@link FindInvitablesTask} is still executing.
- * False otherwise.
- */
- private final AtomicBoolean mInvitablesTaskIsRunning = new AtomicBoolean(false);
-
- private static final int MESSAGE_LOAD_DATA = 0;
- private static final int MESSAGE_PROCESS_BROADCAST_INTENT = 1;
-
- private HandlerThread mListenerThread;
- private Handler mListenerHandler;
-
- private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
- private final Runnable mCheckFilterValidityRunnable = new Runnable () {
- @Override
- public void run() {
- ContactListFilterController.getInstance(mContext).checkFilterValidity(true);
- }
- };
-
- private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Message msg = mListenerHandler.obtainMessage(MESSAGE_PROCESS_BROADCAST_INTENT, intent);
- mListenerHandler.sendMessage(msg);
- }
-
- };
-
- /* A latch that ensures that asynchronous initialization completes before data is used */
- private volatile CountDownLatch mInitializationLatch = new CountDownLatch(1);
-
- private static final Comparator<Account> ACCOUNT_COMPARATOR = new Comparator<Account>() {
- @Override
- public int compare(Account a, Account b) {
- String aDataSet = null;
- String bDataSet = null;
- if (a instanceof AccountWithDataSet) {
- aDataSet = ((AccountWithDataSet) a).dataSet;
- }
- if (b instanceof AccountWithDataSet) {
- bDataSet = ((AccountWithDataSet) b).dataSet;
- }
-
- if (Objects.equal(a.name, b.name) && Objects.equal(a.type, b.type)
- && Objects.equal(aDataSet, bDataSet)) {
- return 0;
- } else if (b.name == null || b.type == null) {
- return -1;
- } else if (a.name == null || a.type == null) {
- return 1;
- } else {
- int diff = a.name.compareTo(b.name);
- if (diff != 0) {
- return diff;
- }
- diff = a.type.compareTo(b.type);
- if (diff != 0) {
- return diff;
- }
-
- // Accounts without data sets get sorted before those that have them.
- if (aDataSet != null) {
- return bDataSet == null ? 1 : aDataSet.compareTo(bDataSet);
- } else {
- return -1;
- }
- }
- }
- };
-
- /**
- * Internal constructor that only performs initial parsing.
- */
- public AccountTypeManagerImpl(Context context) {
- mContext = context;
- mFallbackAccountType = new FallbackAccountType(context);
-
- mAccountManager = AccountManager.get(mContext);
-
- mListenerThread = new HandlerThread("AccountChangeListener");
- mListenerThread.start();
- mListenerHandler = new Handler(mListenerThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MESSAGE_LOAD_DATA:
- loadAccountsInBackground();
- break;
- case MESSAGE_PROCESS_BROADCAST_INTENT:
- processBroadcastIntent((Intent) msg.obj);
- break;
- }
- }
- };
-
- mInvitableAccountTypeCache = new InvitableAccountTypeCache();
-
- // Request updates when packages or accounts change
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addDataScheme("package");
- mContext.registerReceiver(mBroadcastReceiver, filter);
- IntentFilter sdFilter = new IntentFilter();
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
-
- // Request updates when locale is changed so that the order of each field will
- // be able to be changed on the locale change.
- filter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
-
- mAccountManager.addOnAccountsUpdatedListener(this, mListenerHandler, false);
-
- ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this);
-
- mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
- }
-
- @Override
- public void onStatusChanged(int which) {
- mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
- }
-
- public void processBroadcastIntent(Intent intent) {
- mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
- }
-
- /* This notification will arrive on the background thread */
- public void onAccountsUpdated(Account[] accounts) {
- // Refresh to catch any changed accounts
- loadAccountsInBackground();
- }
-
- /**
- * Returns instantly if accounts and account types have already been loaded.
- * Otherwise waits for the background thread to complete the loading.
- */
- void ensureAccountsLoaded() {
- CountDownLatch latch = mInitializationLatch;
- if (latch == null) {
- return;
- }
- while (true) {
- try {
- latch.await();
- return;
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
- }
-
- /**
- * Loads account list and corresponding account types (potentially with data sets). Always
- * called on a background thread.
- */
- protected void loadAccountsInBackground() {
- if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
- Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground start");
- }
- TimingLogger timings = new TimingLogger(TAG, "loadAccountsInBackground");
- final long startTime = SystemClock.currentThreadTimeMillis();
- final long startTimeWall = SystemClock.elapsedRealtime();
-
- // Account types, keyed off the account type and data set concatenation.
- final Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet =
- Maps.newHashMap();
-
- // The same AccountTypes, but keyed off {@link RawContacts#ACCOUNT_TYPE}. Since there can
- // be multiple account types (with different data sets) for the same type of account, each
- // type string may have multiple AccountType entries.
- final Map<String, List<AccountType>> accountTypesByType = Maps.newHashMap();
-
- final List<AccountWithDataSet> allAccounts = Lists.newArrayList();
- final List<AccountWithDataSet> contactWritableAccounts = Lists.newArrayList();
- final List<AccountWithDataSet> groupWritableAccounts = Lists.newArrayList();
- final Set<String> extensionPackages = Sets.newHashSet();
-
- final AccountManager am = mAccountManager;
- final IContentService cs = ContentResolver.getContentService();
-
- try {
- final SyncAdapterType[] syncs = cs.getSyncAdapterTypes();
- final AuthenticatorDescription[] auths = am.getAuthenticatorTypes();
-
- // First process sync adapters to find any that provide contact data.
- for (SyncAdapterType sync : syncs) {
- if (!ContactsContract.AUTHORITY.equals(sync.authority)) {
- // Skip sync adapters that don't provide contact data.
- continue;
- }
-
- // Look for the formatting details provided by each sync
- // adapter, using the authenticator to find general resources.
- final String type = sync.accountType;
- final AuthenticatorDescription auth = findAuthenticator(auths, type);
- if (auth == null) {
- Log.w(TAG, "No authenticator found for type=" + type + ", ignoring it.");
- continue;
- }
-
- AccountType accountType;
- if (GoogleAccountType.ACCOUNT_TYPE.equals(type)) {
- accountType = new GoogleAccountType(mContext, auth.packageName);
- } else if (ExchangeAccountType.isExchangeType(type)) {
- accountType = new ExchangeAccountType(mContext, auth.packageName, type);
- } else {
- // TODO: use syncadapter package instead, since it provides resources
- Log.d(TAG, "Registering external account type=" + type
- + ", packageName=" + auth.packageName);
- accountType = new ExternalAccountType(mContext, auth.packageName, false);
- }
- if (!accountType.isInitialized()) {
- if (accountType.isEmbedded()) {
- throw new IllegalStateException("Problem initializing embedded type "
- + accountType.getClass().getCanonicalName());
- } else {
- // Skip external account types that couldn't be initialized.
- continue;
- }
- }
-
- accountType.accountType = auth.type;
- accountType.titleRes = auth.labelId;
- accountType.iconRes = auth.iconId;
-
- addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
-
- // Check to see if the account type knows of any other non-sync-adapter packages
- // that may provide other data sets of contact data.
- extensionPackages.addAll(accountType.getExtensionPackageNames());
- }
-
- // If any extension packages were specified, process them as well.
- if (!extensionPackages.isEmpty()) {
- Log.d(TAG, "Registering " + extensionPackages.size() + " extension packages");
- for (String extensionPackage : extensionPackages) {
- ExternalAccountType accountType =
- new ExternalAccountType(mContext, extensionPackage, true);
- if (!accountType.isInitialized()) {
- // Skip external account types that couldn't be initialized.
- continue;
- }
- if (!accountType.hasContactsMetadata()) {
- Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
- + " it doesn't have the CONTACTS_STRUCTURE metadata");
- continue;
- }
- if (TextUtils.isEmpty(accountType.accountType)) {
- Log.w(TAG, "Skipping extension package " + extensionPackage + " because"
- + " the CONTACTS_STRUCTURE metadata doesn't have the accountType"
- + " attribute");
- continue;
- }
- Log.d(TAG, "Registering extension package account type="
- + accountType.accountType + ", dataSet=" + accountType.dataSet
- + ", packageName=" + extensionPackage);
-
- addAccountType(accountType, accountTypesByTypeAndDataSet, accountTypesByType);
- }
- }
- } catch (RemoteException e) {
- Log.w(TAG, "Problem loading accounts: " + e.toString());
- }
- timings.addSplit("Loaded account types");
-
- // Map in accounts to associate the account names with each account type entry.
- Account[] accounts = mAccountManager.getAccounts();
- for (Account account : accounts) {
- boolean syncable = false;
- try {
- syncable = cs.getIsSyncable(account, ContactsContract.AUTHORITY) > 0;
- } catch (RemoteException e) {
- Log.e(TAG, "Cannot obtain sync flag for account: " + account, e);
- }
-
- if (syncable) {
- List<AccountType> accountTypes = accountTypesByType.get(account.type);
- if (accountTypes != null) {
- // Add an account-with-data-set entry for each account type that is
- // authenticated by this account.
- for (AccountType accountType : accountTypes) {
- AccountWithDataSet accountWithDataSet = new AccountWithDataSet(
- account.name, account.type, accountType.dataSet);
- allAccounts.add(accountWithDataSet);
- if (accountType.areContactsWritable()) {
- contactWritableAccounts.add(accountWithDataSet);
- }
- if (accountType.isGroupMembershipEditable()) {
- groupWritableAccounts.add(accountWithDataSet);
- }
- }
- }
- }
- }
-
- Collections.sort(allAccounts, ACCOUNT_COMPARATOR);
- Collections.sort(contactWritableAccounts, ACCOUNT_COMPARATOR);
- Collections.sort(groupWritableAccounts, ACCOUNT_COMPARATOR);
-
- timings.addSplit("Loaded accounts");
-
- synchronized (this) {
- mAccountTypesWithDataSets = accountTypesByTypeAndDataSet;
- mAccounts = allAccounts;
- mContactWritableAccounts = contactWritableAccounts;
- mGroupWritableAccounts = groupWritableAccounts;
- mInvitableAccountTypes = findAllInvitableAccountTypes(
- mContext, allAccounts, accountTypesByTypeAndDataSet);
- }
-
- timings.dumpToLog();
- final long endTimeWall = SystemClock.elapsedRealtime();
- final long endTime = SystemClock.currentThreadTimeMillis();
-
- Log.i(TAG, "Loaded meta-data for " + mAccountTypesWithDataSets.size() + " account types, "
- + mAccounts.size() + " accounts in " + (endTimeWall - startTimeWall) + "ms(wall) "
- + (endTime - startTime) + "ms(cpu)");
-
- if (mInitializationLatch != null) {
- mInitializationLatch.countDown();
- mInitializationLatch = null;
- }
- if (Log.isLoggable(Constants.PERFORMANCE_TAG, Log.DEBUG)) {
- Log.d(Constants.PERFORMANCE_TAG, "AccountTypeManager.loadAccountsInBackground finish");
- }
-
- // Check filter validity since filter may become obsolete after account update. It must be
- // done from UI thread.
- mMainThreadHandler.post(mCheckFilterValidityRunnable);
- }
-
- // Bookkeeping method for tracking the known account types in the given maps.
- private void addAccountType(AccountType accountType,
- Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet,
- Map<String, List<AccountType>> accountTypesByType) {
- accountTypesByTypeAndDataSet.put(accountType.getAccountTypeAndDataSet(), accountType);
- List<AccountType> accountsForType = accountTypesByType.get(accountType.accountType);
- if (accountsForType == null) {
- accountsForType = Lists.newArrayList();
- }
- accountsForType.add(accountType);
- accountTypesByType.put(accountType.accountType, accountsForType);
- }
-
- /**
- * Find a specific {@link AuthenticatorDescription} in the provided list
- * that matches the given account type.
- */
- protected static AuthenticatorDescription findAuthenticator(AuthenticatorDescription[] auths,
- String accountType) {
- for (AuthenticatorDescription auth : auths) {
- if (accountType.equals(auth.type)) {
- return auth;
- }
- }
- return null;
- }
-
- /**
- * Return list of all known, contact writable {@link AccountWithDataSet}'s.
- */
- @Override
- public List<AccountWithDataSet> getAccounts(boolean contactWritableOnly) {
- ensureAccountsLoaded();
- return contactWritableOnly ? mContactWritableAccounts : mAccounts;
- }
-
- /**
- * Return the list of all known, group writable {@link AccountWithDataSet}'s.
- */
- public List<AccountWithDataSet> getGroupWritableAccounts() {
- ensureAccountsLoaded();
- return mGroupWritableAccounts;
- }
-
- /**
- * Find the best {@link DataKind} matching the requested
- * {@link AccountType#accountType}, {@link AccountType#dataSet}, and {@link DataKind#mimeType}.
- * If no direct match found, we try searching {@link FallbackAccountType}.
- */
- @Override
- public DataKind getKindOrFallback(AccountType type, String mimeType) {
- ensureAccountsLoaded();
- DataKind kind = null;
-
- // Try finding account type and kind matching request
- if (type != null) {
- kind = type.getKindForMimetype(mimeType);
- }
-
- if (kind == null) {
- // Nothing found, so try fallback as last resort
- kind = mFallbackAccountType.getKindForMimetype(mimeType);
- }
-
- if (kind == null) {
- Log.w(TAG, "Unknown type=" + type + ", mime=" + mimeType);
- }
-
- return kind;
- }
-
- /**
- * Return {@link AccountType} for the given account type and data set.
- */
- @Override
- public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
- ensureAccountsLoaded();
- synchronized (this) {
- AccountType type = mAccountTypesWithDataSets.get(accountTypeWithDataSet);
- return type != null ? type : mFallbackAccountType;
- }
- }
-
- /**
- * @return Unmodifiable map from {@link AccountTypeWithDataSet}s to {@link AccountType}s
- * which support the "invite" feature and have one or more account. This is an unfiltered
- * list. See {@link #getUsableInvitableAccountTypes()}.
- */
- private Map<AccountTypeWithDataSet, AccountType> getAllInvitableAccountTypes() {
- ensureAccountsLoaded();
- return mInvitableAccountTypes;
- }
-
- @Override
- public Map<AccountTypeWithDataSet, AccountType> getUsableInvitableAccountTypes() {
- ensureAccountsLoaded();
- // Since this method is not thread-safe, it's possible for multiple threads to encounter
- // the situation where (1) the cache has not been initialized yet or
- // (2) an async task to refresh the account type list in the cache has already been
- // started. Hence we use {@link AtomicBoolean}s and return cached values immediately
- // while we compute the actual result in the background. We use this approach instead of
- // using "synchronized" because computing the account type list involves a DB read, and
- // can potentially cause a deadlock situation if this method is called from code which
- // holds the DB lock. The trade-off of potentially having an incorrect list of invitable
- // account types for a short period of time seems more manageable than enforcing the
- // context in which this method is called.
-
- // Computing the list of usable invitable account types is done on the fly as requested.
- // If this method has never been called before, then block until the list has been computed.
- if (!mInvitablesCacheIsInitialized.get()) {
- mInvitableAccountTypeCache.setCachedValue(findUsableInvitableAccountTypes(mContext));
- mInvitablesCacheIsInitialized.set(true);
- } else {
- // Otherwise, there is a value in the cache. If the value has expired and
- // an async task has not already been started by another thread, then kick off a new
- // async task to compute the list.
- if (mInvitableAccountTypeCache.isExpired() &&
- mInvitablesTaskIsRunning.compareAndSet(false, true)) {
- new FindInvitablesTask().execute();
- }
- }
-
- return mInvitableAccountTypeCache.getCachedValue();
- }
-
- /**
- * Return all {@link AccountType}s with at least one account which supports "invite", i.e.
- * its {@link AccountType#getInviteContactActivityClassName()} is not empty.
- */
- @VisibleForTesting
- static Map<AccountTypeWithDataSet, AccountType> findAllInvitableAccountTypes(Context context,
- Collection<AccountWithDataSet> accounts,
- Map<AccountTypeWithDataSet, AccountType> accountTypesByTypeAndDataSet) {
- HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap();
- for (AccountWithDataSet account : accounts) {
- AccountTypeWithDataSet accountTypeWithDataSet = account.getAccountTypeWithDataSet();
- AccountType type = accountTypesByTypeAndDataSet.get(accountTypeWithDataSet);
- if (type == null) continue; // just in case
- if (result.containsKey(accountTypeWithDataSet)) continue;
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Type " + accountTypeWithDataSet
- + " inviteClass=" + type.getInviteContactActivityClassName());
- }
- if (!TextUtils.isEmpty(type.getInviteContactActivityClassName())) {
- result.put(accountTypeWithDataSet, type);
- }
- }
- return Collections.unmodifiableMap(result);
- }
-
- /**
- * Return all usable {@link AccountType}s that support the "invite" feature from the
- * list of all potential invitable account types (retrieved from
- * {@link #getAllInvitableAccountTypes}). A usable invitable account type means:
- * (1) there is at least 1 raw contact in the database with that account type, and
- * (2) the app contributing the account type is not disabled.
- *
- * Warning: Don't use on the UI thread because this can scan the database.
- */
- private Map<AccountTypeWithDataSet, AccountType> findUsableInvitableAccountTypes(
- Context context) {
- Map<AccountTypeWithDataSet, AccountType> allInvitables = getAllInvitableAccountTypes();
- if (allInvitables.isEmpty()) {
- return EMPTY_UNMODIFIABLE_ACCOUNT_TYPE_MAP;
- }
-
- final HashMap<AccountTypeWithDataSet, AccountType> result = Maps.newHashMap();
- result.putAll(allInvitables);
-
- final PackageManager packageManager = context.getPackageManager();
- for (AccountTypeWithDataSet accountTypeWithDataSet : allInvitables.keySet()) {
- AccountType accountType = allInvitables.get(accountTypeWithDataSet);
-
- // Make sure that account types don't come from apps that are disabled.
- Intent invitableIntent = MoreContactUtils.getInvitableIntent(accountType,
- SAMPLE_CONTACT_URI);
- if (invitableIntent == null) {
- result.remove(accountTypeWithDataSet);
- continue;
- }
- ResolveInfo resolveInfo = packageManager.resolveActivity(invitableIntent,
- PackageManager.MATCH_DEFAULT_ONLY);
- if (resolveInfo == null) {
- // If we can't find an activity to start for this intent, then there's no point in
- // showing this option to the user.
- result.remove(accountTypeWithDataSet);
- continue;
- }
-
- // Make sure that there is at least 1 raw contact with this account type. This check
- // is non-trivial and should not be done on the UI thread.
- if (!accountTypeWithDataSet.hasData(context)) {
- result.remove(accountTypeWithDataSet);
- }
- }
-
- return Collections.unmodifiableMap(result);
- }
-
- @Override
- public List<AccountType> getAccountTypes(boolean contactWritableOnly) {
- ensureAccountsLoaded();
- final List<AccountType> accountTypes = Lists.newArrayList();
- synchronized (this) {
- for (AccountType type : mAccountTypesWithDataSets.values()) {
- if (!contactWritableOnly || type.areContactsWritable()) {
- accountTypes.add(type);
- }
- }
- }
- return accountTypes;
- }
-
- /**
- * Background task to find all usable {@link AccountType}s that support the "invite" feature
- * from the list of all potential invitable account types. Once the work is completed,
- * the list of account types is stored in the {@link AccountTypeManager}'s
- * {@link InvitableAccountTypeCache}.
- */
- private class FindInvitablesTask extends AsyncTask<Void, Void,
- Map<AccountTypeWithDataSet, AccountType>> {
-
- @Override
- protected Map<AccountTypeWithDataSet, AccountType> doInBackground(Void... params) {
- return findUsableInvitableAccountTypes(mContext);
- }
-
- @Override
- protected void onPostExecute(Map<AccountTypeWithDataSet, AccountType> accountTypes) {
- mInvitableAccountTypeCache.setCachedValue(accountTypes);
- mInvitablesTaskIsRunning.set(false);
- }
- }
-
- /**
- * This cache holds a list of invitable {@link AccountTypeWithDataSet}s, in the form of a
- * {@link Map<AccountTypeWithDataSet, AccountType>}. Note that the cached value is valid only
- * for {@link #TIME_TO_LIVE} milliseconds.
- */
- private static final class InvitableAccountTypeCache {
-
- /**
- * The cached {@link #mInvitableAccountTypes} list expires after this number of milliseconds
- * has elapsed.
- */
- private static final long TIME_TO_LIVE = 60000;
-
- private Map<AccountTypeWithDataSet, AccountType> mInvitableAccountTypes;
-
- private long mTimeLastSet;
-
- /**
- * Returns true if the data in this cache is stale and needs to be refreshed. Returns false
- * otherwise.
- */
- public boolean isExpired() {
- return SystemClock.elapsedRealtime() - mTimeLastSet > TIME_TO_LIVE;
- }
-
- /**
- * Returns the cached value. Note that the caller is responsible for checking
- * {@link #isExpired()} to ensure that the value is not stale.
- */
- public Map<AccountTypeWithDataSet, AccountType> getCachedValue() {
- return mInvitableAccountTypes;
- }
-
- public void setCachedValue(Map<AccountTypeWithDataSet, AccountType> map) {
- mInvitableAccountTypes = map;
- mTimeLastSet = SystemClock.elapsedRealtime();
- }
- }
-}
diff --git a/src/com/android/contacts/model/ContactLoader.java b/src/com/android/contacts/model/ContactLoader.java
index 6259800..3c1fc9d 100644
--- a/src/com/android/contacts/model/ContactLoader.java
+++ b/src/com/android/contacts/model/ContactLoader.java
@@ -43,6 +43,7 @@
import com.android.contacts.GroupMetaData;
import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountTypeWithDataSet;
import com.android.contacts.model.dataitem.DataItem;
diff --git a/src/com/android/contacts/model/RawContact.java b/src/com/android/contacts/model/RawContact.java
index c4e5f3e..9ded00e 100644
--- a/src/com/android/contacts/model/RawContact.java
+++ b/src/com/android/contacts/model/RawContact.java
@@ -26,6 +26,7 @@
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.model.dataitem.DataItem;
diff --git a/src/com/android/contacts/model/RawContactDelta.java b/src/com/android/contacts/model/RawContactDelta.java
index f7ddcc9..1a93cfb 100644
--- a/src/com/android/contacts/model/RawContactDelta.java
+++ b/src/com/android/contacts/model/RawContactDelta.java
@@ -34,6 +34,7 @@
import android.provider.ContactsContract.RawContacts;
import android.util.Log;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.test.NeededForTesting;
import com.google.common.collect.Lists;
diff --git a/src/com/android/contacts/model/RawContactModifier.java b/src/com/android/contacts/model/RawContactModifier.java
index 1b8d0fe..27162a1 100644
--- a/src/com/android/contacts/model/RawContactModifier.java
+++ b/src/com/android/contacts/model/RawContactModifier.java
@@ -47,6 +47,7 @@
import android.util.SparseIntArray;
import com.android.contacts.ContactsUtils;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.util.CommonDateUtils;
import com.android.contacts.editor.EventFieldEditorView;
import com.android.contacts.editor.PhoneticNameEditorView;
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index a585dbe..bc3d53a 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -58,7 +58,7 @@
import com.android.contacts.common.Collapser;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.Contact;
import com.android.contacts.model.ContactLoader;
import com.android.contacts.model.RawContact;
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index 5c584b4..40c29b9 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -38,7 +38,7 @@
import android.widget.RemoteViews;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.model.Contact;
import com.android.contacts.model.ContactLoader;
import com.android.contacts.common.model.account.AccountType;
diff --git a/src/com/android/contacts/util/AccountFilterUtil.java b/src/com/android/contacts/util/AccountFilterUtil.java
index a6df994..1540d04 100644
--- a/src/com/android/contacts/util/AccountFilterUtil.java
+++ b/src/com/android/contacts/util/AccountFilterUtil.java
@@ -27,7 +27,7 @@
import com.android.contacts.R;
import com.android.contacts.list.AccountFilterActivity;
import com.android.contacts.common.list.ContactListFilter;
-import com.android.contacts.list.ContactListFilterController;
+import com.android.contacts.common.list.ContactListFilterController;
/**
* Utility class for account filter manipulation.
diff --git a/src/com/android/contacts/util/AccountSelectionUtil.java b/src/com/android/contacts/util/AccountSelectionUtil.java
index d3c9d14..1d456b3 100644
--- a/src/com/android/contacts/util/AccountSelectionUtil.java
+++ b/src/com/android/contacts/util/AccountSelectionUtil.java
@@ -31,7 +31,7 @@
import android.widget.TextView;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
diff --git a/src/com/android/contacts/util/AccountsListAdapter.java b/src/com/android/contacts/util/AccountsListAdapter.java
index 2740049..a2891a8 100644
--- a/src/com/android/contacts/util/AccountsListAdapter.java
+++ b/src/com/android/contacts/util/AccountsListAdapter.java
@@ -26,7 +26,7 @@
import android.widget.TextView;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
diff --git a/src/com/android/contacts/vcard/ImportVCardActivity.java b/src/com/android/contacts/vcard/ImportVCardActivity.java
index a741ba3..2d6b5d5 100644
--- a/src/com/android/contacts/vcard/ImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/ImportVCardActivity.java
@@ -48,7 +48,7 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.util.AccountSelectionUtil;
import com.android.vcard.VCardEntryCounter;
diff --git a/src/com/android/contacts/vcard/NfcImportVCardActivity.java b/src/com/android/contacts/vcard/NfcImportVCardActivity.java
index c181dfa..3c099cb 100644
--- a/src/com/android/contacts/vcard/NfcImportVCardActivity.java
+++ b/src/com/android/contacts/vcard/NfcImportVCardActivity.java
@@ -32,7 +32,7 @@
import android.util.Log;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.vcard.VCardEntry;
import com.android.vcard.VCardEntryCounter;
diff --git a/src/com/android/contacts/vcard/SelectAccountActivity.java b/src/com/android/contacts/vcard/SelectAccountActivity.java
index 9185244..f74cc8c 100644
--- a/src/com/android/contacts/vcard/SelectAccountActivity.java
+++ b/src/com/android/contacts/vcard/SelectAccountActivity.java
@@ -23,7 +23,7 @@
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
-import com.android.contacts.model.AccountTypeManager;
+import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.util.AccountSelectionUtil;