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.service;
  
  import android.accounts.Account;
  import android.accounts.AccountManager;
  import android.content.AbstractThreadedSyncAdapter;
  import android.content.BroadcastReceiver;
  import android.content.ContentProviderClient;
  import android.content.ContentResolver;
  import android.content.Context;
  import android.content.Intent;
  import android.content.IntentFilter;
  import android.content.SyncResult;
  import android.content.SyncStatusObserver;
  import android.os.Bundle;
  import android.os.Handler;
  import android.provider.ContactsContract;
  
  import com.vodafone360.people.MainApplication;
  import com.vodafone360.people.engine.contactsync.NativeContactsApi;
  import com.vodafone360.people.engine.contactsync.ContactSyncEngine.IContactSyncObserver;
  import com.vodafone360.people.engine.contactsync.ContactSyncEngine.Mode;
  import com.vodafone360.people.engine.contactsync.ContactSyncEngine.State;
  import com.vodafone360.people.service.PersistSettings.InternetAvail;
  import com.vodafone360.people.utils.LogUtils;
  
  /**
   * SyncAdapter implementation which basically just ties in with
   * the old Contacts Sync Engine code for the moment and waits for the sync to be finished.
   * In the future we may want to improve this, particularly if the sync
   * is actually be done in this thread which would also enable disable sync altogether.
   */
  public class SyncAdapter extends AbstractThreadedSyncAdapter implements IContactSyncObserver {
   // TODO: RE-ENABLE SYNC VIA SYSTEM
  //    /**
  //     * Direct access to Sync Engine stored for convenience
  //     */
  //    private final ContactSyncEngine mSyncEngine = EngineManager.getInstance().getContactSyncEngine();
  //    
  //    /**
  //     * Boolean used to remember if we have requested a sync.
  //     * Useful to ignore events 
  //     */
  //    private boolean mPerformSyncRequested = false;
  //    
      
      /**
       * Delay when checking our Sync Setting when there is a authority auto-sync setting change.
       * This waiting time is necessary because in case it is our sync adapter authority setting 
       * that changes we cannot query in the callback because the value is not yet changed!
       */
      private static final int SYNC_SETTING_CHECK_DELAY = 2000;
      
      /**
       * Same as ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS
       * The reason we have this is just because the constant is not publicly defined before 2.2.
       */
      private static final int SYNC_OBSERVER_TYPE_SETTINGS = 1;
      
      /**
       * Application object instance
       */
      private final MainApplication mApplication;
      
      /**
       * Broadcast receiver used to listen for changes in the Master Auto Sync setting
       * intent: com.android.sync.SYNC_CONN_STATUS_CHANGED
       */
      private final BroadcastReceiver mAutoSyncChangeBroadcastReceiver = new BroadcastReceiver() {
          @Override
          public void onReceive(Context context, Intent intent) {
              actOnAutoSyncSettings();
          }
      };
      
      /**
       * Observer for the global sync status setting. 
       * There is no known way to only observe our sync adapter's setting.
       */
      private final SyncStatusObserver mSyncStatusObserver = new SyncStatusObserver() {
          @Override
          public void onStatusChanged(int which) {
              mHandler.postDelayed(mRunnable, SYNC_SETTING_CHECK_DELAY);
          }
      };
      
      /**
       * Handler used to post to a runnable in order to wait 
       * for a short time before checking the sync adapter 
       * authority sync setting after a global change occurs.
       */
      private final Handler mHandler = new Handler();
      
      
      /**
       * Runnable used to post to a runnable in order to wait 
       * for a short time before checking the sync adapter 
       * authority sync setting after a global change occurs.
       * The reason we use this kind of mechanism is because:
       * a) There is an intent(com.android.sync.SYNC_CONN_STATUS_CHANGED) 
       * we can listen to for the Master Auto-sync but,
       * b) The authority auto-sync observer pattern using ContentResolver 
       * listens to EVERY sync adapter setting on the device AND 
       * when the callback is received the value is not yet changed so querying for it is useless.
       */
      private final Runnable mRunnable = new Runnable() {
          @Override
          public void run() {
              actOnAutoSyncSettings();
          }   
      };
              
      public SyncAdapter(Context context, MainApplication application) {
          // Automatically initialized (true) due to PAND-2304
          super(context, true);
          mApplication = application;
          context.registerReceiver(mAutoSyncChangeBroadcastReceiver, new IntentFilter(
              "com.android.sync.SYNC_CONN_STATUS_CHANGED"));
          ContentResolver.addStatusChangeListener(
                  SYNC_OBSERVER_TYPE_SETTINGS, mSyncStatusObserver);
          // Necessary in case of Application update
          forceSyncSettingsInCaseOfAppUpdate();
  
          // Register for sync event callbacks
          // TODO: RE-ENABLE SYNC VIA SYSTEM
          // mSyncEngine.addEventCallback(this);
      }
      
      /**
       * {@inheritDoc}
       */
      @Override
      public void onPerformSync(Account account, Bundle extras, String authority,
          ContentProviderClient provider, SyncResult syncResult) {
          if(extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
              initialize(account, authority);
              return;
          } 
  
          actOnAutoSyncSettings();
  
       // TODO: RE-ENABLE SYNC VIA SYSTEM
  //        try {
  //          synchronized(this) {
  //              mPerformSyncRequested = true;
  //              if(!mSyncEngine.isSyncing()) {
  //                  mSyncEngine.startFullSync();
  //              }
  //              
  //              while(mSyncEngine.isSyncing()) {
  //                  wait(POOLING_WAIT_INTERVAL);
  //              }
  //              mPerformSyncRequested = false;
  //          }
  //        } catch (InterruptedException e) {
  //            e.printStackTrace();
  //        }
      }
      
      
  
      /**
       * @see IContactSyncObserver#onContactSyncStateChange(Mode, State, State)
       */
      @Override
      public void onContactSyncStateChange(Mode mode, State oldState, State newState) {
       // TODO: RE-ENABLE SYNC VIA SYSTEM
  //        synchronized(this) {
  //            /*
  //             * This check is done so that we can also update the native UI 
  //             * when the client devices to sync on it own
  //             */
  //            if(!mPerformSyncRequested && 
  //                    mode != Mode.NONE) {
  //                mPerformSyncRequested = true;
  //                Account account = new Account(
  //                        LoginPreferences.getUsername(), 
  //                        NativeContactsApi2.PEOPLE_ACCOUNT_TYPE);
  //                ContentResolver.requestSync(account, ContactsContract.AUTHORITY, new Bundle());
  //            }
  //        }
      }
  
      /**
       * @see IContactSyncObserver#onProgressEvent(State, int)
       */
      @Override
      public void onProgressEvent(State currentState, int percent) {
          // Nothing to do
      }
  
      /**
       * @see IContactSyncObserver#onSyncComplete(ServiceStatus)
       */
      @Override
      public void onSyncComplete(ServiceStatus status) {
          // Nothing to do
      }
      
      /**
       * Initializes Sync settings for this Sync Adapter
       * @param account The account associated with the initialization
       * @param authority The authority of the content
       */
      public static void initialize(Account account, String authority) {
          ContentResolver.setIsSyncable(account, authority, 1); // > 0 means syncable
          ContentResolver.setSyncAutomatically(account, authority, true);
      }
        
      /**
       * Checks if this Sync Adapter is allowed to Sync Automatically
       * Basically just checking if the Master and its own Auto-sync are on.
       * The Master Auto-sync takes precedence over the authority Auto-sync.
       * @return true if the settings are enabled, false otherwise
       */
      private boolean canSyncAutomatically() {
          Account account = getPeopleAccount();
          if (account == null) {
              // There's no account in the system anyway so 
              // just say true to avoid any issues with the application.
              return true;
          }
          
          boolean masterSyncAuto = ContentResolver.getMasterSyncAutomatically();
          boolean syncAuto = ContentResolver.getSyncAutomatically(account, ContactsContract.AUTHORITY); 
          
          LogUtils.logD("SyncAdapter.canSyncAutomatically() [masterSync=" + 
                  masterSyncAuto + ", syncAuto=" + syncAuto + "]");
          
          return masterSyncAuto && syncAuto;
      }
      
      /**
       * Sets the application data connection setting depending on whether or not 
       * the Sync Adapter is allowed to Sync Automatically.
       * If Automatic Sync is enabled then connection is to online ("always connect")
       * Otherwise connection is set to offline ("manual connect")
       */
      private synchronized void actOnAutoSyncSettings() {
          if(canSyncAutomatically()) {
              // Enable data connection
              mApplication.setInternetAvail(InternetAvail.ALWAYS_CONNECT, false);
          } else {
              // Disable data connection
              mApplication.setInternetAvail(InternetAvail.MANUAL_CONNECT, false);
          }
      }
          
      /**
       * This method is essentially needed to force the sync settings 
       * to a consistent state in case of an Application Update.
       * This is because old versions of the client do not set 
       * the sync adapter to syncable for the contacts authority.
       */
      private void forceSyncSettingsInCaseOfAppUpdate() {
          NativeContactsApi nabApi = NativeContactsApi.getInstance();
          nabApi.setSyncable(true);
          nabApi.setSyncAutomatically(
                  mApplication.getInternetAvail() == InternetAvail.ALWAYS_CONNECT);
      }
      
      /**
       * Gets the first People Account found on the device or
       * null if none is found.
       * Beware! This method is basically duplicate code from 
       * NativeContactsApi2.getPeopleAccount().
       * Duplicating the code was found to be cleanest way to acess the functionality. 
       * @return The Android People account found or null
       */
      private Account getPeopleAccount() {
          android.accounts.Account[] accounts = 
              AccountManager.get(mApplication).getAccountsByType(NativeContactsApi.PEOPLE_ACCOUNT_TYPE_STRING);
          if (accounts != null && accounts.length > 0) {
              Account ret = new Account(accounts[0].name, accounts[0].type);
              return ret;
          }
          return null;
      }
  }
  
  