/* * 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; } }