Turning AccountTypeManager into a "system" service.
It's really not a system service proper. It just
uses the same API and the same dependency injection
mechanism as for system services.
Change-Id: I56e24e0f16642a5ab25e809ee12906d965845379
diff --git a/src/com/android/contacts/CallContactActivity.java b/src/com/android/contacts/CallContactActivity.java
index 76f0892..38e890d 100644
--- a/src/com/android/contacts/CallContactActivity.java
+++ b/src/com/android/contacts/CallContactActivity.java
@@ -18,7 +18,6 @@
import com.android.contacts.interactions.PhoneNumberInteraction;
-import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnDismissListener;
@@ -31,7 +30,7 @@
* An interstitial activity used when the user selects a QSB search suggestion using
* a call button.
*/
-public class CallContactActivity extends Activity implements OnDismissListener {
+public class CallContactActivity extends ContactsActivity implements OnDismissListener {
private PhoneNumberInteraction mPhoneNumberInteraction;
diff --git a/src/com/android/contacts/ContactsActivity.java b/src/com/android/contacts/ContactsActivity.java
new file mode 100644
index 0000000..913bc4c
--- /dev/null
+++ b/src/com/android/contacts/ContactsActivity.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import android.app.Activity;
+
+/**
+ * A common superclass for Contacts activities that handles application-wide services.
+ */
+public abstract class ContactsActivity extends Activity {
+
+ @Override
+ public Object getSystemService(String name) {
+ Object service = super.getSystemService(name);
+ if (service != null) {
+ return service;
+ }
+
+ return getApplicationContext().getSystemService(name);
+ }
+}
diff --git a/src/com/android/contacts/ContactsApplication.java b/src/com/android/contacts/ContactsApplication.java
index 4911fc0..268aa2f 100644
--- a/src/com/android/contacts/ContactsApplication.java
+++ b/src/com/android/contacts/ContactsApplication.java
@@ -28,11 +28,12 @@
public final class ContactsApplication extends Application {
private static InjectedServices sInjectedServices;
+ private AccountTypeManager mAccountTypeManager;
/**
* Overrides the system services with mocks for testing.
*/
- public static void injectContentResolver(InjectedServices services) {
+ public static void injectServices(InjectedServices services) {
sInjectedServices = services;
}
@@ -48,6 +49,25 @@
}
@Override
+ public Object getSystemService(String name) {
+ if (sInjectedServices != null) {
+ Object service = sInjectedServices.getSystemService(name);
+ if (service != null) {
+ return service;
+ }
+ }
+
+ if (AccountTypeManager.ACCOUNT_TYPE_SERVICE.equals(name)) {
+ if (mAccountTypeManager == null) {
+ mAccountTypeManager = AccountTypeManager.createAccountTypeManager(this);
+ }
+ return mAccountTypeManager;
+ }
+
+ return super.getSystemService(name);
+ }
+
+ @Override
public void onCreate() {
super.onCreate();
diff --git a/src/com/android/contacts/activities/AttachPhotoActivity.java b/src/com/android/contacts/activities/AttachPhotoActivity.java
index f5114fd..65d24c3 100644
--- a/src/com/android/contacts/activities/AttachPhotoActivity.java
+++ b/src/com/android/contacts/activities/AttachPhotoActivity.java
@@ -16,11 +16,11 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import com.android.contacts.model.ExchangeAccountType;
import com.android.contacts.model.GoogleAccountType;
-import android.app.Activity;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -47,7 +47,7 @@
* image that is handed to it through the cropper to make the image the proper
* size and give the user a chance to use the face detector.
*/
-public class AttachPhotoActivity extends Activity {
+public class AttachPhotoActivity extends ContactsActivity {
private static final int REQUEST_PICK_CONTACT = 1;
private static final int REQUEST_CROP_PHOTO = 2;
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index d155be0..fc96140 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -17,6 +17,7 @@
package com.android.contacts.activities;
import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import com.android.contacts.detail.ContactDetailFragment;
import com.android.contacts.interactions.ContactDeletionInteraction;
@@ -81,7 +82,7 @@
* Displays a list to browse contacts. For xlarge screens, this also displays a detail-pane on
* the right
*/
-public class ContactBrowserActivity extends Activity
+public class ContactBrowserActivity extends ContactsActivity
implements View.OnCreateContextMenuListener, ActionBarAdapter.Listener,
DialogManager.DialogShowingViewActivity,
ContactListFilterController.ContactListFilterListener, ProviderStatusListener {
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index 7a3a3b9..ca26280 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -17,13 +17,13 @@
package com.android.contacts.activities;
import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
import com.android.contacts.detail.ContactDetailFragment;
import com.android.contacts.interactions.ContactDeletionInteraction;
import android.accounts.Account;
-import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
import android.content.Intent;
@@ -35,7 +35,7 @@
import java.util.ArrayList;
-public class ContactDetailActivity extends Activity {
+public class ContactDetailActivity extends ContactsActivity {
private static final String TAG = "ContactDetailActivity";
private ContactDetailFragment mFragment;
diff --git a/src/com/android/contacts/activities/ContactEditorActivity.java b/src/com/android/contacts/activities/ContactEditorActivity.java
index 04d0f44..00e0ab0 100644
--- a/src/com/android/contacts/activities/ContactEditorActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorActivity.java
@@ -16,6 +16,7 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
import com.android.contacts.editor.ContactEditorFragment;
@@ -44,8 +45,8 @@
import java.util.ArrayList;
-public class ContactEditorActivity extends Activity implements
- DialogManager.DialogShowingViewActivity {
+public class ContactEditorActivity extends ContactsActivity
+ implements DialogManager.DialogShowingViewActivity {
private static final String TAG = "ContactEditorActivity";
public static final String ACTION_JOIN_COMPLETED = "joinCompleted";
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index e0a05cc..121b74d 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -16,6 +16,7 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import com.android.contacts.list.ContactEntryListFragment;
import com.android.contacts.list.ContactPickerFragment;
@@ -29,7 +30,6 @@
import com.android.contacts.list.PostalAddressPickerFragment;
import com.android.contacts.widget.ContextMenuAdapter;
-import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
@@ -47,7 +47,7 @@
* Displays a list of contacts (or phone numbers or postal addresses) for the
* purposes of selecting one.
*/
-public class ContactSelectionActivity extends Activity
+public class ContactSelectionActivity extends ContactsActivity
implements View.OnCreateContextMenuListener, OnQueryChangeListener, OnClickListener {
private static final String TAG = "ContactSelectionActivity";
diff --git a/src/com/android/contacts/activities/ContactsFrontDoor.java b/src/com/android/contacts/activities/ContactsFrontDoor.java
index e12bbf1..214382d 100644
--- a/src/com/android/contacts/activities/ContactsFrontDoor.java
+++ b/src/com/android/contacts/activities/ContactsFrontDoor.java
@@ -16,14 +16,14 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.DialtactsActivity;
-import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
-public class ContactsFrontDoor extends Activity {
+public class ContactsFrontDoor extends ContactsActivity {
public static final String EXTRA_FRONT_DOOR = "front_door";
@Override
diff --git a/src/com/android/contacts/activities/JoinContactActivity.java b/src/com/android/contacts/activities/JoinContactActivity.java
index d1645cb..2d205e6 100644
--- a/src/com/android/contacts/activities/JoinContactActivity.java
+++ b/src/com/android/contacts/activities/JoinContactActivity.java
@@ -17,14 +17,13 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import com.android.contacts.list.ContactEntryListFragment;
import com.android.contacts.list.JoinContactListFragment;
import com.android.contacts.list.OnContactPickerActionListener;
-import android.app.Activity;
import android.app.Fragment;
-import android.app.FragmentTransaction;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -37,7 +36,7 @@
/**
* An activity that shows a list of contacts that can be joined with the target contact.
*/
-public class JoinContactActivity extends Activity {
+public class JoinContactActivity extends ContactsActivity {
private static final String TAG = "JoinContactActivity";
diff --git a/src/com/android/contacts/activities/NonPhoneActivity.java b/src/com/android/contacts/activities/NonPhoneActivity.java
index d963cd5..d37f21f 100644
--- a/src/com/android/contacts/activities/NonPhoneActivity.java
+++ b/src/com/android/contacts/activities/NonPhoneActivity.java
@@ -16,6 +16,7 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
import android.app.Activity;
@@ -23,8 +24,8 @@
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
-import android.content.Intent;
import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.Contacts;
@@ -35,7 +36,7 @@
* Activity that intercepts DIAL and VIEW intents for phone numbers for devices that can not
* be used as a phone. This allows the user to see the phone number
*/
-public class NonPhoneActivity extends Activity {
+public class NonPhoneActivity extends ContactsActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/src/com/android/contacts/activities/ShowOrCreateActivity.java b/src/com/android/contacts/activities/ShowOrCreateActivity.java
index 1502d9a..4cd4394 100755
--- a/src/com/android/contacts/activities/ShowOrCreateActivity.java
+++ b/src/com/android/contacts/activities/ShowOrCreateActivity.java
@@ -16,6 +16,7 @@
package com.android.contacts.activities;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
import com.android.contacts.util.Constants;
@@ -53,8 +54,8 @@
* {@link Intent#ACTION_SEARCH}.
* </ul>
*/
-public final class ShowOrCreateActivity extends Activity implements
- NotifyingAsyncQueryHandler.AsyncQueryListener {
+public final class ShowOrCreateActivity extends ContactsActivity
+ implements NotifyingAsyncQueryHandler.AsyncQueryListener {
static final String TAG = "ShowOrCreateActivity";
static final boolean LOGD = false;
diff --git a/src/com/android/contacts/model/AccountTypeManager.java b/src/com/android/contacts/model/AccountTypeManager.java
index 4355986..cbfc750 100644
--- a/src/com/android/contacts/model/AccountTypeManager.java
+++ b/src/com/android/contacts/model/AccountTypeManager.java
@@ -53,7 +53,38 @@
* Singleton holder for all parsed {@link AccountType} available on the
* system, typically filled through {@link PackageManager} queries.
*/
-public class AccountTypeManager extends BroadcastReceiver
+public abstract class AccountTypeManager {
+
+ public static final String ACCOUNT_TYPE_SERVICE = "contactAccountTypes";
+
+ /**
+ * 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) {
+ return (AccountTypeManager) context.getSystemService(ACCOUNT_TYPE_SERVICE);
+ }
+
+ public static synchronized AccountTypeManager createAccountTypeManager(Context context) {
+ return new AccountTypeManagerImpl(context);
+ }
+
+ public abstract ArrayList<Account> getAccounts(boolean writableOnly);
+
+ public abstract AccountType getAccountType(String accountType);
+
+ /**
+ * Find the best {@link DataKind} matching the requested
+ * {@link AccountType#accountType} and {@link DataKind#mimeType}. If no
+ * direct match found, we try searching {@link FallbackAccountType}.
+ */
+ public DataKind getKindOrFallback(String accountType, String mimeType, Context context) {
+ final AccountType type = getAccountType(accountType);
+ return type == null ? null : type.getKindForMimetype(mimeType);
+ }
+}
+
+class AccountTypeManagerImpl extends AccountTypeManager
implements OnAccountsUpdateListener, SyncStatusObserver {
private static final String TAG = "ContactAccountTypes";
@@ -72,11 +103,19 @@
private HandlerThread mListenerThread;
private Handler mListenerHandler;
+ 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 AccountTypeManager sInstance = null;
-
private static final Comparator<Account> ACCOUNT_COMPARATOR = new Comparator<Account>() {
@Override
@@ -90,24 +129,9 @@
};
/**
- * 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 synchronized AccountTypeManager getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new AccountTypeManager(context.getApplicationContext());
- }
- return sInstance;
- }
-
- public static void injectAccountTypes(AccountTypeManager injectedAccountTypes) {
- sInstance = injectedAccountTypes;
- }
-
- /**
* Internal constructor that only performs initial parsing.
*/
- private AccountTypeManager(Context context) {
+ public AccountTypeManagerImpl(Context context) {
mContext = context;
mAccountManager = AccountManager.get(mContext);
@@ -132,16 +156,16 @@
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addDataScheme("package");
- mContext.registerReceiver(this, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
IntentFilter sdFilter = new IntentFilter();
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(this, sdFilter);
+ 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(this, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter);
mAccountManager.addOnAccountsUpdatedListener(this, mListenerHandler, false);
@@ -150,20 +174,6 @@
mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
}
- /** @hide exposed for unit tests */
- public AccountTypeManager(AccountType... accountTypes) {
- for (AccountType accountType : accountTypes) {
- mAccountTypes.put(accountType.accountType, accountType);
- }
- mInitializationLatch = null;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- Message msg = mListenerHandler.obtainMessage(MESSAGE_PROCESS_BROADCAST_INTENT, intent);
- mListenerHandler.sendMessage(msg);
- }
-
@Override
public void onStatusChanged(int which) {
mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
@@ -319,6 +329,7 @@
/**
* Return list of all known, writable {@link Account}'s.
*/
+ @Override
public ArrayList<Account> getAccounts(boolean writableOnly) {
ensureAccountsLoaded();
return writableOnly ? mWritableAccounts : mAccounts;
@@ -327,10 +338,9 @@
/**
* Find the best {@link DataKind} matching the requested
* {@link AccountType#accountType} and {@link DataKind#mimeType}. If no
- * direct match found, we try searching {@link #mFallbackAccountType}.
- * When fourceRefresh is set to true, cache is refreshed and inflation of each
- * EditField will occur.
+ * direct match found, we try searching {@link FallbackAccountType}.
*/
+ @Override
public DataKind getKindOrFallback(String accountType, String mimeType, Context context) {
ensureAccountsLoaded();
DataKind kind = null;
@@ -356,6 +366,7 @@
/**
* Return {@link AccountType} for the given account type.
*/
+ @Override
public AccountType getAccountType(String accountType) {
ensureAccountsLoaded();
synchronized (this) {
diff --git a/src/com/android/contacts/test/FragmentTestActivity.java b/src/com/android/contacts/test/FragmentTestActivity.java
index f95b284..34b1556 100644
--- a/src/com/android/contacts/test/FragmentTestActivity.java
+++ b/src/com/android/contacts/test/FragmentTestActivity.java
@@ -16,16 +16,16 @@
package com.android.contacts.test;
+import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
-import android.app.Activity;
import android.os.Bundle;
/**
* An activity that is used for testing fragments. A unit test starts this
* activity, adds a fragment and then tests the fragment.
*/
-public class FragmentTestActivity extends Activity {
+public class FragmentTestActivity extends ContactsActivity {
@Override
public void onCreate(Bundle savedInstanceState) {