/* * * * Copyright (C) 2014 Open Whisper Systems * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * / */ package org.anhonesteffort.flock; import android.accounts.Account; import android.app.NotificationManager; import android.app.Service; import android.content.ContentProviderClient; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.support.v4.app.NotificationCompat; import android.util.Log; import android.util.Pair; import org.anhonesteffort.flock.sync.addressbook.AddressbookSyncScheduler; import org.anhonesteffort.flock.sync.addressbook.ContactCopiedListener; import org.anhonesteffort.flock.sync.addressbook.LocalContactCollection; import java.util.LinkedList; import java.util.List; /** * Programmer: rhodey */ public class ContactCopyService extends Service implements ContactCopiedListener { private static final String TAG = "org.anhonesteffort.flock.ContactCopyService"; protected static final String ACTION_QUEUE_ACCOUNT_FOR_COPY = "ContactCopyService.ACTION_QUEUE_ACCOUNT_FOR_COPY"; protected static final String ACTION_START_COPY = "ContactCopyService.ACTION_START_COPY"; private static final String KEY_INTENT = "ContactCopyService.KEY_INTENT"; private static final String KEY_ACCOUNT_WITH_ERROR = "ContactCopyService.KEY_ACCOUNT_WITH_ERROR"; private static final String KEY_ACCOUNT_ERROR_COUNT = "ContactCopyService.KEY_ACCOUNT_ERROR_COUNT"; protected static final String KEY_FROM_ACCOUNT = "ContactCopyService.KEY_FROM_ACCOUNT"; protected static final String KEY_TO_ACCOUNT = "ContactCopyService.KEY_TO_ACCOUNT"; protected static final String KEY_CONTACT_COUNT = "ContactCopyService.KEY_CONTACT_COUNT"; private final List<Pair<Account, Account>> accountsForCopy = new LinkedList<Pair<Account, Account>>(); private Looper serviceLooper; private ServiceHandler serviceHandler; private NotificationManager notifyManager; private NotificationCompat.Builder notificationBuilder; private List<Bundle> accountErrors; private int countContactsToCopy = 0; private int countContactsCopied = 0; private int countContactCopiesFailed = 0; private void handleInitializeNotification() { Log.d(TAG, "handleInitializeNotification()"); notificationBuilder.setContentTitle(getString(R.string.notification_contact_import)) .setContentText(getString(R.string.notification_importing_contacts)) .setProgress(100, 1, false) .setSmallIcon(R.drawable.flock_actionbar_icon); startForeground(1023, notificationBuilder.build()); } private void handleContactCopied(Account fromAccount) { countContactsCopied++; Log.d(TAG, "handleContactCopied() contacts copied: " + countContactsCopied); notificationBuilder .setContentText(getString(R.string.notification_importing_contacts_from, ((fromAccount != null) ? fromAccount.name : getString(R.string.local_storage)))) .setProgress(countContactsToCopy, countContactsCopied + countContactCopiesFailed, false); notifyManager.notify(1023, notificationBuilder.build()); } private void handleContactCopyFailed(Account fromAccount) { countContactCopiesFailed++; Log.d(TAG, "handleContactCopyFailed() contact copies failed: " + countContactCopiesFailed); boolean accountWasFound = false; for (Bundle accountError : accountErrors) { Account accountWithError = accountError.getParcelable(KEY_ACCOUNT_WITH_ERROR); Integer errorCount = accountError.getInt(KEY_ACCOUNT_ERROR_COUNT, -1); if (accountWithError != null && errorCount > 0) { if (fromAccount.equals(accountWithError)) { accountError.putInt(KEY_ACCOUNT_ERROR_COUNT, errorCount + 1); accountWasFound = true; break; } } } if (!accountWasFound) { Bundle errorBundle = new Bundle(); errorBundle.putParcelable(KEY_ACCOUNT_WITH_ERROR, fromAccount); errorBundle.putInt(KEY_ACCOUNT_ERROR_COUNT, 1); accountErrors.add(errorBundle); } notificationBuilder .setContentText(getString(R.string.notification_importing_contacts_from, fromAccount.name)) .setProgress(countContactsToCopy, countContactsCopied + countContactCopiesFailed, false); notifyManager.notify(1023, notificationBuilder.build()); } private void handleCopyComplete() { Log.d(TAG, "handleCopyComplete()"); stopForeground(false); stopSelf(); } @Override public void onDestroy() { Log.d(TAG, "onDestroy()"); if (countContactCopiesFailed == 0) { notificationBuilder .setProgress(0, 0, false) .setContentText(getString(R.string.notification_import_complete_copied_contacts, countContactsCopied)); } else { notificationBuilder .setProgress(0, 0, false) .setContentText(getString(R.string.notification_import_complete_copied_contacts_failed, countContactsCopied, countContactCopiesFailed)); } notifyManager.notify(1023, notificationBuilder.build()); } private void handleQueueAccountForCopy(Intent intent) { Log.d(TAG, "handleQueueAccountForCopy()"); Account fromAccount = intent.getParcelableExtra(KEY_FROM_ACCOUNT); Account toAccount = intent.getParcelableExtra(KEY_TO_ACCOUNT); Integer contactCount = intent.getIntExtra(KEY_CONTACT_COUNT, -1); if (fromAccount == null || toAccount == null || contactCount < 0) { Log.e(TAG, "failed to parse to account, from account, or contact count from intent extras."); return; } accountsForCopy.add(new Pair<Account, Account>(fromAccount, toAccount)); countContactsToCopy += contactCount; Log.d(TAG, "contacts to copy: " + countContactsToCopy); } private void handleStartCopy() { Log.d(TAG, "handleStartCopy()"); handleInitializeNotification(); ContentProviderClient client = getBaseContext().getContentResolver() .acquireContentProviderClient(AddressbookSyncScheduler.CONTENT_AUTHORITY); for (Pair<Account, Account> copyPair : accountsForCopy) { LocalContactCollection fromCollection = null; if (!copyPair.first.type.equals(getString(R.string.local_storage))) fromCollection = new LocalContactCollection(getBaseContext(), client, copyPair.first, "hack"); else fromCollection = new LocalContactCollection(getBaseContext(), client, null, "hack"); try { fromCollection.copyToAccount(copyPair.second, this); } catch (RemoteException e) { ErrorToaster.handleShowError(getBaseContext(), e); return; } } handleCopyComplete(); } @Override public void onContactCopied(Account fromAccount, Account toAccount) { handleContactCopied(fromAccount); } @Override public void onContactCopyFailed(Exception e, Account fromAccount, Account toAccount) { Log.e(TAG, "onContactCopyFailed() from " + ((fromAccount != null) ? fromAccount.name : getString(R.string.local_storage)) + " to " + ((toAccount != null) ? toAccount.name : getString(R.string.local_storage)), e); handleContactCopyFailed(fromAccount); } @Override public void onCreate() { HandlerThread thread = new HandlerThread("ContactCopyService", HandlerThread.NORM_PRIORITY); thread.start(); serviceLooper = thread.getLooper(); serviceHandler = new ServiceHandler(serviceLooper); notifyManager = (NotificationManager)getBaseContext().getSystemService(Context.NOTIFICATION_SERVICE); notificationBuilder = new NotificationCompat.Builder(getBaseContext()); accountErrors = new LinkedList<Bundle>(); } private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { Log.d(TAG, "handleMessage()"); Intent intent = msg.getData().getParcelable(KEY_INTENT); if (intent != null && intent.getAction() != null) { if (intent.getAction().equals(ACTION_QUEUE_ACCOUNT_FOR_COPY)) handleQueueAccountForCopy(intent); else if (intent.getAction().equals(ACTION_START_COPY)) handleStartCopy(); else Log.e(TAG, "received intent with unknown action"); } else Log.e(TAG, "received message with null intent or intent action"); } } @Override public int onStartCommand(Intent intent, int flags, int startId) { Message msg = serviceHandler.obtainMessage(); msg.getData().putParcelable(KEY_INTENT, intent); serviceHandler.sendMessage(msg); return START_STICKY; } @Override public IBinder onBind(Intent intent) { Log.d(TAG, "onBind()"); return null; } }