blob: c019a23af653bc5e48859c19a522affed312111e [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* 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
* distributed 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 the specific language governing permissions and
* limitations under the License.
*/
package com.cooliris.picasa;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicBoolean;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Service;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Process;
public final class PicasaService extends Service {
public static final String ACTION_SYNC = "com.cooliris.picasa.action.SYNC";
public static final String ACTION_PERIODIC_SYNC = "com.cooliris.picasa.action.PERIODIC_SYNC";
public static final String ACCOUNT_TYPE = "com.google";
public static final String SERVICE_NAME = "lh2";
public static final String FEATURE_SERVICE_NAME = "service_" + SERVICE_NAME;
public static final String KEY_TYPE = "com.cooliris.SYNC_TYPE";
public static final String KEY_ID = "com.cooliris.SYNC_ID";
public static final int TYPE_USERS = 0;
public static final int TYPE_USERS_ALBUMS = 1;
public static final int TYPE_ALBUM_PHOTOS = 2;
private final HandlerThread mSyncThread = new HandlerThread("PicasaSyncThread");
private final Handler mSyncHandler;
private static final AtomicBoolean sSyncPending = new AtomicBoolean(false);
public static void requestSync(Context context, int type, long id) {
Bundle extras = new Bundle();
extras.putInt(KEY_TYPE, type);
extras.putLong(KEY_ID, id);
Account[] accounts = PicasaApi.getAccounts(context);
for (Account account : accounts) {
ContentResolver.requestSync(account, PicasaContentProvider.AUTHORITY, extras);
}
// context.startService(new Intent(context,
// PicasaService.class).putExtras(extras));
}
public PicasaService() {
super();
mSyncThread.start();
mSyncHandler = new Handler(mSyncThread.getLooper());
mSyncHandler.post(new Runnable() {
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
});
}
private static PicasaContentProvider getContentProvider(Context context) {
ContentResolver cr = context.getContentResolver();
ContentProviderClient client = cr.acquireContentProviderClient(PicasaContentProvider.AUTHORITY);
return (PicasaContentProvider) client.getLocalContentProvider();
}
@Override
public int onStartCommand(final Intent intent, int flags, final int startId) {
mSyncHandler.post(new Runnable() {
public void run() {
performSync(PicasaService.this, null, intent.getExtras(), new SyncResult());
stopSelf(startId);
}
});
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return new PicasaSyncAdapter(getApplicationContext()).getSyncAdapterBinder();
}
@Override
public void onDestroy() {
mSyncThread.quit();
}
public static boolean performSync(Context context, Account account, Bundle extras, SyncResult syncResult) {
// Skip if another sync is pending.
if (!sSyncPending.compareAndSet(false, true)) {
return false;
}
// Perform the sync.
performSyncImpl(context, account, extras, syncResult);
// Mark sync as complete and notify all waiters.
sSyncPending.set(false);
synchronized (sSyncPending) {
sSyncPending.notifyAll();
}
return true;
}
public static void waitForPerformSync() {
synchronized (sSyncPending) {
while (sSyncPending.get()) {
try {
// Wait for the sync to complete.
sSyncPending.wait();
} catch (InterruptedException e) {
// Stop waiting if interrupted.
break;
}
}
}
}
private static void performSyncImpl(Context context, Account account, Bundle extras, SyncResult syncResult) {
// Initialize newly added accounts to sync by default.
String authority = PicasaContentProvider.AUTHORITY;
if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE, false)) {
if (account != null && ContentResolver.getIsSyncable(account, authority) < 0) {
try {
ContentResolver.setIsSyncable(account, authority, getIsSyncable(context, account) ? 1 : 0);
} catch (OperationCanceledException e) {
} catch (IOException e) {
}
}
return;
}
// Do nothing if sync is disabled for this account. TODO: is this
// blocked in PicasaContentProvider too?
if (account != null && ContentResolver.getIsSyncable(account, authority) < 0) {
++syncResult.stats.numSkippedEntries;
return;
}
// Get the type of sync operation and the entity ID, if applicable.
// Default to synchronize all.
int type = extras.getInt(PicasaService.KEY_TYPE, PicasaService.TYPE_USERS_ALBUMS);
long id = extras.getLong(PicasaService.KEY_ID, -1);
// Get the content provider instance and reload the list of user
// accounts.
PicasaContentProvider provider = getContentProvider(context);
provider.reloadAccounts();
// Restrict sync to either a specific account or all accounts.
provider.setActiveSyncAccount(account);
// Perform the desired sync operation.
switch (type) {
case PicasaService.TYPE_USERS:
provider.syncUsers(syncResult);
break;
case PicasaService.TYPE_USERS_ALBUMS:
provider.syncUsersAndAlbums(true, syncResult);
break;
case PicasaService.TYPE_ALBUM_PHOTOS:
provider.syncAlbumPhotos(id, true, syncResult);
break;
default:
throw new IllegalArgumentException();
}
}
private static boolean getIsSyncable(Context context, Account account) throws IOException, OperationCanceledException {
try {
Account[] picasaAccounts = AccountManager.get(context).getAccountsByTypeAndFeatures(ACCOUNT_TYPE,
new String[] { FEATURE_SERVICE_NAME }, null /* callback */, null /* handler */).getResult();
for (Account picasaAccount : picasaAccounts) {
if (account.equals(picasaAccount)) {
return true;
}
}
return false;
} catch (AuthenticatorException e) {
throw new IOException(e.getMessage());
}
}
}