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) {