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