// Copyright 2015 The Project Buendia Authors
//
// 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 distrib-
// uted 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
// specific language governing permissions and limitations under the License.
package org.projectbuendia.client.sync;
import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.app.Service;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SyncStatusObserver;
import android.os.Bundle;
import android.os.IBinder;
import com.google.common.eventbus.EventBus;
import org.projectbuendia.client.AppSettings;
import org.projectbuendia.client.BuildConfig;
import org.projectbuendia.client.sync.SyncAdapter.SyncOption;
import org.projectbuendia.client.sync.SyncAdapter.SyncPhase;
import org.projectbuendia.client.providers.Contracts;
import org.projectbuendia.client.utils.Logger;
import javax.inject.Inject;
/**
* A {@link Service} that manages the app's sync account, including static
* functions for account registration and sync requests. For a detailed
* description of the app's sync architecture, see:
* https://github.com/projectbuendia/buendia/wiki/Client-Sync
*/
public class SyncAccountService extends Service {
public static final String ACCOUNT_NAME = "sync";
private static final Logger LOG = Logger.create();
private static final long SYNC_PERIOD = 5*60; // 5 minutes (in seconds)
@Inject static AppSettings sSettings;
private Authenticator mAuthenticator;
/** Sets up the sync account for this app. */
public static void initialize(Context context) {
if (createAccount(context) || !sSettings.getSyncAccountInitialized()) {
startFullSync();
sSettings.setSyncAccountInitialized(true);
}
}
/**
* Creates the sync account for this app if it doesn't already exist.
* @return true if a new account was created
*/
private static boolean createAccount(Context context) {
Account account = getAccount();
AccountManager accountManager = (AccountManager) context.getSystemService(ACCOUNT_SERVICE);
if (accountManager.addAccountExplicitly(account, null, null)) {
// Enable automatic sync for the account with a period of SYNC_PERIOD.
ContentResolver.setIsSyncable(account, Contracts.CONTENT_AUTHORITY, 1);
ContentResolver.setSyncAutomatically(account, Contracts.CONTENT_AUTHORITY, true);
Bundle b = new Bundle();
b.putBoolean(SyncOption.FULL_SYNC.name(), true);
ContentResolver.addPeriodicSync(account, Contracts.CONTENT_AUTHORITY, b, SYNC_PERIOD);
return true;
}
return false;
}
/** Starts a full sync. */
public static void startFullSync() {
Bundle b = new Bundle();
// Request aggressively that the sync should start straight away.
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
// Fetch everything
b.putBoolean(SyncOption.FULL_SYNC.name(), true);
LOG.i("Requesting full sync");
ContentResolver.requestSync(getAccount(), Contracts.CONTENT_AUTHORITY, b);
}
/** Gets the app's sync account (call initialize() before using this). */
public static Account getAccount() {
return new Account(ACCOUNT_NAME, BuildConfig.ACCOUNT_TYPE);
}
/** Starts an sync of just the observations and orders. */
public static void startObservationsAndOrdersSync() {
// Start by canceling any existing syncs, which may delay this one.
ContentResolver.cancelSync(getAccount(), Contracts.CONTENT_AUTHORITY);
Bundle b = new Bundle();
// Request aggressively that the sync should start straight away.
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
// Fetch just the newly added observations.
b.putBoolean(SyncPhase.SYNC_OBSERVATIONS.name(), true);
b.putBoolean(SyncPhase.SYNC_ORDERS.name(), true);
LOG.i("Requesting incremental observations / orders sync");
ContentResolver.requestSync(getAccount(), Contracts.CONTENT_AUTHORITY, b);
}
@Override public void onCreate() {
LOG.i("Service created");
mAuthenticator = new Authenticator(this);
}
@Override public void onDestroy() {
LOG.i("Service destroyed");
}
@Override public IBinder onBind(Intent intent) {
return mAuthenticator.getIBinder();
}
/** A dummy authenticator. */
private static class Authenticator extends AbstractAccountAuthenticator {
public Authenticator(Context context) {
super(context);
}
public Bundle addAccount(
AccountAuthenticatorResponse r, String s1, String s2, String[] ss, Bundle b) {
return null;
}
public Bundle confirmCredentials(AccountAuthenticatorResponse r, Account a, Bundle b) {
return null;
}
public Bundle editProperties(AccountAuthenticatorResponse r, String s) {
throw new UnsupportedOperationException();
}
public Bundle getAuthToken(AccountAuthenticatorResponse r, Account a, String s, Bundle b) {
throw new UnsupportedOperationException();
}
public String getAuthTokenLabel(String s) {
throw new UnsupportedOperationException();
}
public Bundle updateCredentials(
AccountAuthenticatorResponse r, Account a, String s, Bundle b) {
throw new UnsupportedOperationException();
}
public Bundle hasFeatures(AccountAuthenticatorResponse r, Account a, String[] ss) {
throw new UnsupportedOperationException();
}
}
}