content: /*
   * CDDL HEADER START
   *
   * The contents of this file are subject to the terms of the Common Development
   * and Distribution License (the "License").
   * You may not use this file except in compliance with the License.
   *
   * You can obtain a copy of the license at
   * src/com/vodafone360/people/VODAFONE.LICENSE.txt or
   * http://github.com/360/360-Engine-for-Android
   * See the License for the specific language governing permissions and
   * limitations under the License.
   *
   * When distributing Covered Code, include this CDDL HEADER in each file and
   * include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
   * If applicable, add the following below this CDDL HEADER, with the fields
   * enclosed by brackets "[]" replaced with your own identifying information:
   * Portions Copyright [yyyy] [name of copyright owner]
   *
   * CDDL HEADER END
   *
   * Copyright 2010 Vodafone Sales & Services Ltd.  All rights reserved.
   * Use is subject to license terms.
   */
  
  package com.vodafone360.people.engine.contactsync;
  
  import java.security.InvalidParameterException;
  
  import android.content.ContentResolver;
  import android.content.Context;
  import android.text.TextUtils;
  
  import com.vodafone360.people.utils.LogUtils;
  import com.vodafone360.people.utils.VersionUtils;
  
  /**
   * Class that provides an abstraction layer for accessing the Native Contacts API.
   * The underlying API to be used should be the most suitable for the SDK version of the device.
   */
  public abstract class NativeContactsApi { 
  
      /**
       * 360 client account type.
       */
      protected static final int PEOPLE_ACCOUNT_TYPE = 1;
  
      /**
       * Google account type.
       */
      protected static final int GOOGLE_ACCOUNT_TYPE = 2;
  
      /**
       * Vendor specific type.
       */
      protected static final int PHONE_ACCOUNT_TYPE = 3;
  
      /**
       * Account type for 360 People in the Native Accounts. 
       * MUST be a copy of type in 'res/xml/authenticator.xml' 
       */
      public static final String PEOPLE_ACCOUNT_TYPE_STRING = "com.vodafone360.people.android.account";
  
      /**
       * Google account, there can be more than one of these
       */
      protected static final String GOOGLE_ACCOUNT_TYPE_STRING = "com.google";
      
      /**
       * HTC vendor specific account type for phone contacts.
       * Current indication is that this account type is HTC Sense specific.
       */
      private static final String HTC_PHONE_ACCOUNT_TYPE_STRING = 
          "com.htc.android.pcsc";
      
      /**
       * Samsung vendor specific Account type String for phone contacts. 
       */
      private static final String SAMSUNG_PHONE_ACCOUNT_TYPE_STRING = 
          "vnd.sec.contact.phone";
      
      /**
       * Motorola vendor specific Account type String for phone contacts.
       * Current indication is that this account type is Moto Blur specific.
       */
      private static final String MOTOROLA_PHONE_ACCOUNT_TYPE_STRING = 
          "com.motorola.blur.service.bsutils.MOTHER_USER_CREDS_TYPE";
      
      /**
       * Sony Ericsson vendor specific Account type for phone contacts.
       */
      private static final String SONY_ERICSSON_PHONE_ACCOUNT_TYPE_STRING = 
          "com.sonyericsson.localcontacts";
  
      /**
       * There are devices with custom contact applications. For them we need
       * special handling of contacts.
       */
      protected static final String[] VENDOR_SPECIFIC_ACCOUNTS = {
              HTC_PHONE_ACCOUNT_TYPE_STRING, 
              SAMSUNG_PHONE_ACCOUNT_TYPE_STRING,
              MOTOROLA_PHONE_ACCOUNT_TYPE_STRING,
              SONY_ERICSSON_PHONE_ACCOUNT_TYPE_STRING
      };
      
      /**
       * {@link NativeContactsApi} the singleton instance providing access to the correct Contacts API interface
       */    
      private static NativeContactsApi sInstance;
      
      /**
       * {@link Context} to be used by the Instance
       */
      protected Context mContext;
  
      /**
       * {@link ContentResolver} be used by the Instance
       */
      protected ContentResolver mCr;
  
      /**
       * Sadly have to have this so that only one organization may be read from a
       * NAB Contact
       */
      protected boolean mHaveReadOrganization = false;
  
      /**
       * Sadly have to have this because Organization detail is split into two
       * details in CAB
       */
      protected int mMarkedOrganizationIndex = -1;
  
      /**
       * Sadly have to have this because Organization detail is split into two
       * details in CAB
       */
      protected int mMarkedTitleIndex = -1;
  
      /**
       * Create NativeContactsApi singleton instance for later usage. The instance
       * can retrieved by calling getInstance(). * The instance can be destroyed
       * by calling destroyInstance()
       * 
       * @see NativeContactsApi#getInstance()
       * @see NativeContactsApi#destroyInstance()
       * @param context The context to be used by the singleton
       */
      public static void createInstance(Context context) {
          LogUtils.logW("NativeContactsApi.createInstance()");
          String className;
  
          if (VersionUtils.is2XPlatform()) {
              className = "NativeContactsApi2";
              LogUtils.logD("Using 2.X Native Contacts API");
          } else {
              className = "NativeContactsApi1";
              LogUtils.logD("Using 1.X Native Contacts API");
          }
  
          try {
              Class<? extends NativeContactsApi> clazz = Class.forName(
                      NativeContactsApi.class.getPackage().getName() + "." + className).asSubclass(
                      NativeContactsApi.class);
              sInstance = clazz.newInstance();
          } catch (Exception e) {
              throw new IllegalStateException("NativeContactsApi.createInstance()"
                      + "Error creating a subclass of NativeContactsApi", e);
          }
  
          sInstance.mContext = context;
          sInstance.mCr = context.getContentResolver();
          // Initialize the instance now (got Context and Content Resolver)
          sInstance.initialize();
      }
  
      /**
       * Destroy NativeContactsApi singleton instance if created. The instance can
       * be recreated by calling createInstance()
       * 
       * @see NativeContactsApi#createInstance()
       */
      public static void destroyInstance() {
          if (sInstance != null) {
              sInstance.mCr = null;
              sInstance.mContext = null;
              sInstance = null;
          }
      }
  
      /**
       * Retrieves singleton instance providing access to the native contacts api.
       * 
       * @return {@link NativeContactsApi} appropriate subclass instantiation
       */
      public static NativeContactsApi getInstance() {
          if (sInstance == null) {
              throw new InvalidParameterException("Please call "
                      + "NativeContactsApi.createInstance() "
                      + "before NativeContactsApi.getInstance()");
          }
  
          return sInstance;
      }
  
      /**
       * This Account class represents an available account on the device where
       * the native synchronization can be performed.
       */
      public static class Account {
  
          /**
           * The name of the account.
           */
          private String mName;
  
          /**
           * The type of the account.
           */
          private String mType;
  
          /**
           * The Constructor.
           * 
           * @param name the account name
           * @param type the account type
           */
          public Account(String name, String type) {
              mName = name;
              mType = type;
          }
  
          /**
           * Gets the name of the account.
           * 
           * @return the account name
           */
          public String getName() {
              return mName;
          }
  
          /**
           * Gets the type of the accounts.
           * 
           * @return the account type
           */
          public String getType() {
              return mType;
          }
  
          /**
           * Checks if this account is a People Account
           * 
           * @return true if this account is a People Account, false if not
           */
          public boolean isPeopleAccount() {
              return TextUtils.equals(mType, PEOPLE_ACCOUNT_TYPE_STRING);
          }
  
          /**
           * Returns a String representation of the account.
           */
          public String toString() {
              return "Account: name=" + mName + ", type=" + mType;
          }
      }
  
      /**
       * The Observer interface to receive notifications about changes in the
       * native address book.
       */
      public static interface ContactsObserver {
  
          /**
           * Call-back to notify that a change happened in the native address
           * book.
           */
          void onChange();
      }
  
      /**
       * Method meant to be called only just after createInstance() is invoked.
       * This method effectively acts as a replacement for the constructor because
       * of the use of reflection.
       */
      protected abstract void initialize();
  
      /**
       * Registers a content observer. Note: the method only supports one observer
       * at a time.
       * 
       * @param observer ContactsObserver currently observing native address book
       *            changes
       * @throws RuntimeException if a new observer is being registered without
       *             having unregistered the previous one
       */
      public abstract void registerObserver(ContactsObserver observer);
  
      /**
       * Unregister the previously registered content observer.
       */
      public abstract void unregisterObserver();
  
      /**
       * Fetches all the existing Accounts on the device. Only supported on 2.X.
       * The 1.X implementation always returns null.
       * 
       * @return An array containing all the Accounts on the device, or null if
       *         none exist
       */
      public abstract Account[] getAccounts();
  
      /**
       * Fetches all the existing Accounts on the device corresponding to the
       * provided Type Only supported on 2.X. The 1.X implementation always
       * returns null.
       * 
       * @param type The Type of Account to fetch
       * @return An array containing all the Accounts of the provided Type, or
       *         null if none exist
       */
      public abstract Account[] getAccountsByType(int type);
  
      /**
       * Adds the currently logged in user account to the NAB accounts. Only
       * supported on 2.X. The 1.X implementation does nothing.
       * 
       * @return true if successful, false if not
       */
      public abstract boolean addPeopleAccount(String username);
      
      /**
       * Checks if there is a People Account in the NAB Accounts. Only supported
       * on 2.X The 1.X implementation does nothing.
       * 
       * @return true if an account exists, false if not.
       */
      public abstract boolean isPeopleAccountCreated();
      
      /**
       * Removes the (first found) People Account from the NAB accounts. Only
       * supported on 2.X. The 1.X implementation does nothing.
       */
      public abstract void removePeopleAccount();
  
      /**
       * Retrieves a list of contact IDs for a specific account. In 1.X devices
       * only the null account is supported, i.e., a non null account always uses
       * null as a return value.
       * 
       * @param account The account to get contact IDs from (may be null)
       * @return List of contact IDs from the native address book
       */
      public abstract long[] getContactIds(Account account);
  
      /**
       * Gets data for one Contact.
       * 
       * @param nabContactId Native ID for the contact
       * @return A {@link ContactChange} array with contact's data or null
       */
      public abstract ContactChange[] getContact(long nabContactId);
  
      /**
       * Adds a contact. Note that the returned ID data will be the same size of
       * ccList plus one change containing the NAB Contact ID (at the first
       * position). The remaining IDs correspond to the details in the original
       * change list. Null IDs among these remaining IDs are possible when these
       * correspond to unsupported details. On 1.X Devices only a null account
       * parameter is expected.
       * 
       * @param account Account to be associated with the added contact (may be
       *            null).
       * @param ccList The Contact data as a {@link ContactChange} array
       * @return A {@link ContactChange} array with contact's new ID data or null
       *         in case of failure.
       */
      public abstract ContactChange[] addContact(Account account, ContactChange[] ccList);
  
      /**
       * Updates an existing contact. The returned ID data will be the same size
       * of the ccList. However, if a null or empty ccList is passed as an
       * argument then null is returned. Null IDs among the ID data are also
       * possible when these correspond to unsupported details.
       * 
       * @param ccList The Contact update data as a {@link ContactChange} array
       * @return A {@link ContactChange} array with the contact's new ID data or
       *         null
       */
      public abstract ContactChange[] updateContact(ContactChange[] ccList);
  
      /**
       * Removes a contact
       * 
       * @param nabContactId Native ID of the contact to remove
       */
      public abstract void removeContact(long nabContactId);
      
      /**
       * Used to retrieve the master Auto-sync setting for the system.
       * Method only exists to get around platform fragmentation.
       * Note that this method always returns true for 1.X devices!
       * @return true if the Master Auto-sync is enabled, false otherwise
       */
      public abstract boolean getMasterSyncAutomatically();
  
      /**
       * Used to set the system syncable functionality on and off.
       * This is really only useful for 2.X devices.
       * @param syncable if true, false otherwise 
       */
      public abstract void setSyncable(boolean syncable);
      
      /**
       * Used to forcefully set the underlying Sync adapter to be Automatically Syncable or not.
       * This is really only useful for 2.X devices and for System UE.
       * @param syncAutomatically if true Sync is Automatic, false otherwise
       */
      public abstract void setSyncAutomatically(boolean syncAutomatically);
  
      /**
       * Checks whether or not a {@link ContactChange} key is supported. Results
       * may vary in 1.X and 2.X
       * 
       * @param key Key to check for support
       * @return true if supported, false if not
       */
      public abstract boolean isKeySupported(int key);
      /**
       * Checks if this account is a vendor specific one. All vendor specific
       * accounts are held in the VENDOR_SPECIFIC_ACCOUNTS array.
       * 
       * @param type to checks for
       * @return true if vendor specific account, false otherwise
       */
      public boolean isVendorSpecificAccount(String type) {
          for (String vendorSpecificType : VENDOR_SPECIFIC_ACCOUNTS) {
              if (vendorSpecificType.equals(type)) {
                  return true;
              }
          }
          return false;
      }
    
  }
  