I've built an Account sync adapter to show contacts from the app in local contact book.
There is a fake authonticator that provides account. Also account is syncable
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
This account is displayed in all accounts on the device:
I've tried to trigger onPerformSync by system - from settings in menu press 'sync now' and programatically:
public static void triggerRefresh() {
Bundle b = new Bundle();
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
Account account = AccountAuthenticatorService.GetAccount();
if (ContentResolver.isSyncPending(account, ContactsContract.AUTHORITY) ||
ContentResolver.isSyncActive(account, ContactsContract.AUTHORITY)) {
ContentResolver.cancelSync(account, ContactsContract.AUTHORITY);
}
ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
ContentResolver.requestSync(
account, // Sync account
ContactsContract.AUTHORITY,authority
b);
}
It works fine on emulator, but on several devices (sony, samsung) it not triggered at all (I've tried to log smth in onPerformSync method but never see this log).
I've tried to find such problem, but nothing helps, I can't make onPerformSync force to be called.
What the main difference between emulator and device according to syncAdapter?
Finally I've found the reason of this issue:
onPerformSync method will never be called until you don't have internet connection. On several devices and emulators there was an internet connection but on others you can't trigger this method.
Related
Can anyone help me out in understanding, How WhatsApp and imo apps are syncing contacts immediately after add/delete/updating contact.
I have tried following method:
Tried to register ContentObserver in service so that we can get contact which is updated.
If we able to deploy a ContentObserver to contacts database, how do we differentiate manual and programatic updating of a contact.
Note: I am using Sync adaptor to sync the contacts with server but not able to get trigger points for sync process.
Please help me.
Sync adapter has an option to force sync or immediate sync . From documentation the method is similar to the snippet below
public void onRefreshButtonClick(View v) {
...
// Pass the settings flags by inserting them in a bundle
Bundle settingsBundle = new Bundle();
settingsBundle.putBoolean(
ContentResolver.SYNC_EXTRAS_MANUAL, true);
settingsBundle.putBoolean(
ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
/*
* Request the sync for the default account, authority, and
* manual sync settings
*/
ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
}
Overview
I follwed Google's tutorial on using SyncAdapter without using ContentProvider, Authenticator..etc. It works perfectly when I call onPerformSync(...) when I need to do an "upload" to the server via de SyncAdapter.
Now, as you can imagine, I need to do downloads from the server as well (yes I understand that it would be better to use Google's Cloud Messaing system, but this is the set up I was given, and I can't change that). For that, instead of doing periodical syncs, I want to make use of the "Network tickle" Android system carries out when there is a network available. For that I state the following:
ContentResolver.setIsSyncable(accounts[0], AUTHORITY, 1);
ContentResolver.setSyncAutomatically(accounts[0], AUTHORITY, true);
But my SyncAdapter is just not getting called. Looking into other stackOverFlow questions, there seem to be a problem if targetting API 10 or below with SyncAdapter and that you must add an account explicitly before calling the before methods. So I ended up with this:
AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
Account[] accounts = accountManager.getAccounts();
if(accounts.length == 0){ //ADD DUMMY ACCOUNT
Account newAccount = new Account(ACCOUNT, ACCOUNT_TYPE);
ContentResolver.setIsSyncable(accounts[0], AUTHORITY, 1);
ContentResolver.setSyncAutomatically(accounts[0], AUTHORITY, true);
accountManager.addAccountExplicitly(newAccount, null, null);
}else{
accounts = accountManager.getAccounts();
ContentResolver.setIsSyncable(accounts[0], AUTHORITY, 1);
ContentResolver.setSyncAutomatically(accounts[0], AUTHORITY, true);
}
Now this code gets executed when the user signs in, or if the application was killed and is started up again. I am wondering, should I call setIsSyncable and setSyncAutomatically only when I add the dummyAccount the very first time?
Also, part of the "goodiness" of the SyncAdapter is that it will keep on making the calls in case of an exception. But I don't quite understand how this goes about, so instead I have this:
private void profileUpdate(){
TableAccounts db = TableAccounts.getInstance(getContext());
boolean isRecordDirty = db.isRecordDirty(signedInUser);
if(isRecordDirty){
if(server.upDateUserProfile(signedInUser)){
db.cleanDirtyRecord(signedInUser);
turnOffPeriodicSync();
}else{
this.turnOnPeriodicSync(this.sync_bundle);
}
}else
turnOffPeriodicSync();
}
As you can see, depending on the result of my upload to the server, I turn on or off a periodic sync.
Since the accountManager.getAccounts[] return every account on the device, I think nothing guarantee that the account[0] is your app's account (aka, has the ACCOUNT_TYPE of your package name).
-- You could call addAccountExplicitly() in any case, if it is existed, then nothing happens.
Account account = new Account(ACCOUNT, ACCOUNT_TYPE);
AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
accountManager.addAccountExplicitly(account, null, null)
context.getContentResolver().setSyncAutomatically(account, AUTHORITY, true);
Disclaimer: I might be mistaken.
On top everything you did, you also have to call ContentResolver.requestSync() from within your app every time you need a sync to run. The sync will not run immediately though, because Android is trying to cluster network activity.
Or you can use Googles Cloud Messaging API to request a sync, but I don't know a whole lot about that.
I've implemented a SyncAdapter pretty much as outlined here (with a dummy account) and using one of my content providers (I have one per table). As far as I can tell the sync code is fine. If I go into "Settings -> Accounts" select the dummy account I've created and then the "Sync Now" menu item my adapter is run by the system. All it does ATM is log the fact that it was called.
But trying to run it from my own "Sync Now" menu option (see below on my main activity) doesn't. Is there a way of getting the sync adapter to run immediately given that we have a WiFi connection?
private boolean actionSynchronize() {
if (mLogger.isTraceEnabled())
mLogger.trace("Synchronising now!");
// Pass the settings flags by inserting them in a bundle
Bundle settingsBundle = new Bundle();
settingsBundle.putBoolean(
ContentResolver.SYNC_EXTRAS_MANUAL, true);
settingsBundle.putBoolean(
ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
/*
* Request the sync for the default account, authority, and
* manual sync settings
*/
ContentResolver.requestSync(mAccount, AUTHORITY, settingsBundle);
return true;
}
Please try with this.
Bundle b = new Bundle();
// Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(
GenericAccountService.GetAccount(), // Sync account
FeedContract.CONTENT_AUTHORITY, // Content authority
b); // Extras
I'm having a situation with SyncAdapter I don't know how to fix.
I'm using periodic syncs. The onPerformSync method just logs some info for me to know that the process is working (no calls to notifyChanges in content providers or anything else).
The project consists of two apps: The first one creates a user account (for testing purposes only). The second holds the sync adapter. Note that this is perfectly legal for the scope of the project.
I first install the app with the account.
I can see the account has been created.
Then I install the app with the sync adapter and the first time it runs the synchronization hangs. Seeing the account sync settings, the spinner icon is continuously running and no log messages are registered (meaning it does not reach onPerformSync).
However, I can cancel the sync in the Settings and then the sync process starts working normally. This means the wiring between Account, Content Provider and SyncService is properly set.
I'm aware that adding/removing an account triggers other sync processes so I let a good lapse of time to go before installing the app with the sync adapter.
Any hints on why this is happening?
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAccountManager = AccountManager.get(this);
// No worries here. The account exists and it's the one I want
Account[] accounts = mAccountManager.getAccountsByType(Constants.ACCOUNT_TYPE);
// Just first account for TESTING purposes
if (accounts != null && accounts.length > 0)
account = accounts[0];
else {
Log.e(TAG, "No accounts set!!");
return;
}
// Set sync for this account.
Bundle extras = new Bundle();
extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
extras.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false);
extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
ContentResolver.setIsSyncable(account, authority, 1); // Mandatory since 3.1
// Allows the provider to sync when internet connection is back
ContentResolver.setSyncAutomatically(account, authority, true);
// Add a periodic synchronization
ContentResolver.addPeriodicSync(account, authority, extras, POLL_FREQUENCY);
}
EDIT
I found out that calling cancel on the sync, makes it work. Not the best solution but it fixes the problem by now. I put this line combined with a "isFirstUse" flag.
ContentResolver.cancelSync(account, authority);
I'm using AbstractThreadedSyncAdapter to synchronize some data from my Android's phone with a server. When the user logs in with the phone's application I perform a first synchronization so the user has all the data ready in the phone before he starts working with my application. I tested it with eclipse's emulator and it works fine, but when I test it on my phone the function onPerformSync takes too long to be called (like a minute or more).
This is the code I use in the Authenticator Activity:
Bundle params = new Bundle();
params.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
params.putBoolean(ContentResolver.SYNC_EXTRAS_DO_NOT_RETRY, false);
params.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false);
ContentResolver.addPeriodicSync(account, "com.pfc.authority", params, 30);
ContentResolver.setSyncAutomatically(account, "com.pfc.authority", true);
ContentResolver.requestSync(account,"com.pfc.authority",params);
The constructor of my AbstractThreadedSyncAdapter is called really fast, but then my application remains waiting for the sync to be complete, and like I said the onPerformSync function takes too much time to start, I don't know why my phone takes too long to call it.
This is the log (filtered for my application). I placed Log.d in the constructor and in onPerformSyn function:
11:47:49 SweetSyncAdapter: constructor
11:48:01 SweetSyncAdapter: constructor
11:48:36 SweetSyncAdapter: onPerformSync
I looked the full log (without filtering it) and there is no errors or warnings while executing my application, but I saw that there was another syncrhonization going on (music, calendar, etc.). Can this be the problem?
I tested it with two different ROMs (2.3.6 JVU and now ICS), and it's the same. Does anybody know why this happens?
PD: Right now I saw that the same happens with logout (I perform a syncrhonization when the user logouts), and the phone has been 5 minutes waiting for the onPerformSync to be called).
After I created an account I request a sync with following lines.
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_FORCE, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
ContentResolver.requestSync(account, authority, bundle);
I think the SYNC_EXTRAS_FORCE will help you