Android Open Source - sms-backup-plus Restore Task






From Project

Back to project page sms-backup-plus.

License

The source code is released under:

Apache License

If you think the Android project sms-backup-plus listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.zegoggles.smssync.service;
/*  ww  w  . j  a va  2s .c o m*/
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.CallLog;
import android.util.Log;
import com.fsck.k9.mail.AuthenticationFailedException;
import com.fsck.k9.mail.FetchProfile;
import com.fsck.k9.mail.Message;
import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.store.XOAuth2AuthenticationFailedException;
import com.squareup.otto.Subscribe;
import com.zegoggles.smssync.App;
import com.zegoggles.smssync.Consts;
import com.zegoggles.smssync.SmsConsts;
import com.zegoggles.smssync.auth.TokenRefreshException;
import com.zegoggles.smssync.auth.TokenRefresher;
import com.zegoggles.smssync.mail.BackupImapStore;
import com.zegoggles.smssync.mail.DataType;
import com.zegoggles.smssync.mail.MessageConverter;
import com.zegoggles.smssync.service.state.RestoreState;
import com.zegoggles.smssync.service.state.SmsSyncState;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static com.zegoggles.smssync.App.LOCAL_LOGV;
import static com.zegoggles.smssync.App.TAG;
import static com.zegoggles.smssync.mail.DataType.CALLLOG;
import static com.zegoggles.smssync.mail.DataType.SMS;
import static com.zegoggles.smssync.service.state.SmsSyncState.CALC;
import static com.zegoggles.smssync.service.state.SmsSyncState.CANCELED_RESTORE;
import static com.zegoggles.smssync.service.state.SmsSyncState.ERROR;
import static com.zegoggles.smssync.service.state.SmsSyncState.FINISHED_RESTORE;
import static com.zegoggles.smssync.service.state.SmsSyncState.LOGIN;
import static com.zegoggles.smssync.service.state.SmsSyncState.RESTORE;
import static com.zegoggles.smssync.service.state.SmsSyncState.UPDATING_THREADS;

class RestoreTask extends AsyncTask<RestoreConfig, RestoreState, RestoreState> {
    private Set<String> smsIds = new HashSet<String>();
    private Set<String> callLogIds = new HashSet<String>();
    private Set<String> uids = new HashSet<String>();

    private final SmsRestoreService service;
    private final ContentResolver resolver;
    private final MessageConverter converter;
    private final TokenRefresher tokenRefresher;

    public RestoreTask(SmsRestoreService service,
                       MessageConverter converter,
                       ContentResolver resolver,
                       TokenRefresher tokenRefresher) {
        this.service = service;
        this.converter = converter;
        this.resolver = resolver;
        this.tokenRefresher = tokenRefresher;
    }

    @Override
    protected void onPreExecute() {
        App.bus.register(this);
    }

    @Subscribe public void userCanceled(UserCanceled canceled) {
        cancel(false);
    }

    @NotNull protected RestoreState doInBackground(RestoreConfig... params) {
        if (params == null || params.length == 0) throw new IllegalArgumentException("No config passed");
        RestoreConfig config = params[0];

        if (!config.restoreSms && !config.restoreCallLog) {
            return new RestoreState(FINISHED_RESTORE, 0, 0, 0, 0, null, null);
        } else {
            try {
                service.acquireLocks();
                return restore(config);
            } finally {
                service.releaseLocks();
            }
        }
    }

    private RestoreState restore(RestoreConfig config) {
        final BackupImapStore imapStore = config.imapStore;

        int currentRestoredItem = config.currentRestoredItem;
        try {
            publishProgress(LOGIN);
            imapStore.checkSettings();

            publishProgress(CALC);

            final List<Message> msgs = new ArrayList<Message>();

            if (config.restoreSms) {
                msgs.addAll(imapStore.getFolder(SMS).getMessages(config.maxRestore, config.restoreOnlyStarred, null));
            }
            if (config.restoreCallLog) {
                msgs.addAll(imapStore.getFolder(CALLLOG).getMessages(config.maxRestore, config.restoreOnlyStarred, null));
            }

            final int itemsToRestoreCount = config.maxRestore <= 0 ? msgs.size() : Math.min(msgs.size(), config.maxRestore);

            if (itemsToRestoreCount > 0) {
                for (; currentRestoredItem < itemsToRestoreCount && !isCancelled(); currentRestoredItem++) {
                    DataType dataType = importMessage(msgs.get(currentRestoredItem));

                    msgs.set(currentRestoredItem, null); // help gc
                    publishProgress(new RestoreState(RESTORE, currentRestoredItem, itemsToRestoreCount, 0, 0, dataType, null));
                    if (currentRestoredItem % 50 == 0) {
                        //clear cache periodically otherwise SD card fills up
                        service.clearCache();
                    }
                }
                if (!isCancelled()) {
                    publishProgress(UPDATING_THREADS);
                    updateAllThreads();
                }
            } else {
                Log.d(TAG, "nothing to restore");
            }

            final int restoredCount = smsIds.size() + callLogIds.size();
            return new RestoreState(isCancelled() ? CANCELED_RESTORE : FINISHED_RESTORE,
                    currentRestoredItem,
                    itemsToRestoreCount,
                    restoredCount,
                    uids.size() - restoredCount, null, null);
        } catch (XOAuth2AuthenticationFailedException e) {
            return handleAuthError(config, currentRestoredItem, e);
        } catch (AuthenticationFailedException e) {
            return transition(ERROR, e);
        } catch (MessagingException e) {
            Log.e(TAG, "error", e);
            return transition(ERROR, e);
        } catch (IllegalStateException e) {
            // usually memory problems (Couldn't init cursor window)
            return transition(ERROR, e);
        } finally {
            imapStore.closeFolders();
        }
    }

    private RestoreState handleAuthError(RestoreConfig config, int currentRestoredItem, XOAuth2AuthenticationFailedException e) {
        if (e.getStatus() == 400) {
            Log.d(TAG, "need to perform xoauth2 token refresh");
            if (config.tries < 1) {
                try {
                    tokenRefresher.refreshOAuth2Token();
                    // we got a new token, let's retry one more time - we need to pass in a new store object
                    // since the auth params on it are immutable
                    return restore(config.retryWithStore(currentRestoredItem, service.getBackupImapStore()));
                } catch (MessagingException ignored) {
                    Log.w(TAG, ignored);
                } catch (TokenRefreshException refreshException) {
                    Log.w(TAG, refreshException);
                }
            } else {
                Log.w(TAG, "no new token obtained, giving up");
            }
        } else {
            Log.w(TAG, "unexpected xoauth status code " + e.getStatus());
        }
        return transition(ERROR, e);
    }

    private void publishProgress(SmsSyncState smsSyncState) {
        publishProgress(smsSyncState, null);
    }

    private void publishProgress(SmsSyncState smsSyncState, Exception exception) {
        publishProgress(transition(smsSyncState, exception));
    }

    private RestoreState transition(SmsSyncState smsSyncState, Exception exception) {
        return service.getState().transition(smsSyncState, exception);
    }

    @Override
    protected void onPostExecute(RestoreState result) {
        if (result != null) {
            Log.d(TAG, "finished (" + result + "/" + uids.size() + ")");
            post(result);
        }
        App.bus.unregister(this);
    }

    @Override
    protected void onCancelled() {
        Log.d(TAG, "restore canceled by user");
        post(transition(CANCELED_RESTORE, null));
        App.bus.unregister(this);
    }

    @Override
    protected void onProgressUpdate(RestoreState... progress) {
        if (progress != null && progress.length > 0 && !isCancelled()) {
            post(progress[0]);
        }
    }

    private void post(RestoreState changed) {
        if (changed == null) return;
        App.bus.post(changed);
    }

    private DataType importMessage(Message message) {
        uids.add(message.getUid());

        FetchProfile fp = new FetchProfile();
        fp.add(FetchProfile.Item.BODY);
        DataType dataType = null;
        try {
            if (LOCAL_LOGV) Log.v(TAG, "fetching message uid " + message.getUid());
            message.getFolder().fetch(Arrays.asList(message), fp, null);
            dataType = converter.getDataType(message);
            //only restore sms+call log for now
            switch (dataType) {
                case CALLLOG:
                    importCallLog(message);
                    break;
                case SMS:
                    importSms(message);
                    break;
                default:
                    if (LOCAL_LOGV) Log.d(TAG, "ignoring restore of type: " + dataType);
            }

        } catch (MessagingException e) {
            Log.e(TAG, "error", e);
        } catch (IllegalArgumentException e) {
            // http://code.google.com/p/android/issues/detail?id=2916
            Log.e(TAG, "error", e);
        } catch (IOException e) {
            Log.e(TAG, "error", e);
        }
        return dataType;
    }

    private void importSms(final Message message) throws IOException, MessagingException {
        if (LOCAL_LOGV) Log.v(TAG, "importSms(" + message + ")");
        final ContentValues values = converter.messageToContentValues(message);
        final Integer type = values.getAsInteger(SmsConsts.TYPE);

        // only restore inbox messages and sent messages - otherwise sms might get sent on restore
        if (type != null && (type == SmsConsts.MESSAGE_TYPE_INBOX || type == SmsConsts.MESSAGE_TYPE_SENT) && !smsExists(values)) {

            final Uri uri = resolver.insert(Consts.SMS_PROVIDER, values);
            if (uri != null) {
                smsIds.add(uri.getLastPathSegment());
                Long timestamp = values.getAsLong(SmsConsts.DATE);

                if (timestamp != null && SMS.getMaxSyncedDate(service) < timestamp) {
                    SMS.setMaxSyncedDate(service, timestamp);
                }

                if (LOCAL_LOGV) Log.v(TAG, "inserted " + uri);
            }
        } else {
            if (LOCAL_LOGV) Log.d(TAG, "ignoring sms");
        }
    }

    private void importCallLog(final Message message) throws MessagingException, IOException {
        if (LOCAL_LOGV) Log.v(TAG, "importCallLog(" + message + ")");
        final ContentValues values = converter.messageToContentValues(message);
        if (!callLogExists(values)) {
            final Uri uri = resolver.insert(Consts.CALLLOG_PROVIDER, values);
            if (uri != null) callLogIds.add(uri.getLastPathSegment());
        } else {
            if (LOCAL_LOGV) Log.d(TAG, "ignoring call log");
        }
    }

    private boolean callLogExists(ContentValues values) {
        Cursor c = resolver.query(Consts.CALLLOG_PROVIDER,
            new String[] { "_id" },
            "date = ? AND number = ? AND duration = ? AND type = ?",
            new String[]{
                values.getAsString(CallLog.Calls.DATE),
                values.getAsString(CallLog.Calls.NUMBER),
                values.getAsString(CallLog.Calls.DURATION),
                values.getAsString(CallLog.Calls.TYPE)
            },
            null
        );
        boolean exists = false;
        if (c != null) {
            exists = c.getCount() > 0;
            c.close();
        }
        return exists;
    }

    private boolean smsExists(ContentValues values) {
        // just assume equality on date+address+type
        Cursor c = resolver.query(Consts.SMS_PROVIDER,
            new String[] {"_id" },
            "date = ? AND address = ? AND type = ?",
            new String[] {
                values.getAsString(SmsConsts.DATE),
                values.getAsString(SmsConsts.ADDRESS),
                values.getAsString(SmsConsts.TYPE)
            },
            null
        );

        boolean exists = false;
        if (c != null) {
            exists = c.getCount() > 0;
            c.close();
        }
        return exists;
    }

    private void updateAllThreads() {
        // thread dates + states might be wrong, we need to force a full update
        // unfortunately there's no direct way to do that in the SDK, but passing a
        // negative conversation id to delete should to the trick
        Log.d(TAG, "updating threads");
        resolver.delete(Uri.parse("content://sms/conversations/-1"), null, null);
        Log.d(TAG, "finished");
    }

    protected Set<String> getSmsIds() {
        return smsIds;
    }
}




Java Source Code List

com.zegoggles.smssync.App.java
com.zegoggles.smssync.Consts.java
com.zegoggles.smssync.MmsConsts.java
com.zegoggles.smssync.SmsConsts.java
com.zegoggles.smssync.activity.Dialogs.java
com.zegoggles.smssync.activity.MainActivity.java
com.zegoggles.smssync.activity.StatusPreference.java
com.zegoggles.smssync.activity.auth.AccountManagerAuthActivity.java
com.zegoggles.smssync.activity.auth.WebAuthActivity.java
com.zegoggles.smssync.activity.donation.DonationActivity.java
com.zegoggles.smssync.auth.TokenRefreshException.java
com.zegoggles.smssync.auth.TokenRefresher.java
com.zegoggles.smssync.auth.XOAuthConsumer.java
com.zegoggles.smssync.calendar.CalendarAccessorPost40.java
com.zegoggles.smssync.calendar.CalendarAccessorPre40.java
com.zegoggles.smssync.calendar.CalendarAccessor.java
com.zegoggles.smssync.compat.ComposeSmsActivity.java
com.zegoggles.smssync.compat.HeadlessSmsSendService.java
com.zegoggles.smssync.compat.MmsReceiver.java
com.zegoggles.smssync.compat.SmsReceiver.java
com.zegoggles.smssync.compat.package-info.java
com.zegoggles.smssync.contacts.ContactAccessorPost20.java
com.zegoggles.smssync.contacts.ContactAccessorPre20.java
com.zegoggles.smssync.contacts.ContactAccessor.java
com.zegoggles.smssync.contacts.ContactGroupIds.java
com.zegoggles.smssync.contacts.ContactGroup.java
com.zegoggles.smssync.contacts.Group.java
com.zegoggles.smssync.mail.AllTrustedSocketFactory.java
com.zegoggles.smssync.mail.Attachment.java
com.zegoggles.smssync.mail.BackupImapStore.java
com.zegoggles.smssync.mail.BackupStoreConfig.java
com.zegoggles.smssync.mail.CallFormatter.java
com.zegoggles.smssync.mail.ConversionResult.java
com.zegoggles.smssync.mail.DataType.java
com.zegoggles.smssync.mail.HeaderGenerator.java
com.zegoggles.smssync.mail.Headers.java
com.zegoggles.smssync.mail.MessageConverter.java
com.zegoggles.smssync.mail.MessageGenerator.java
com.zegoggles.smssync.mail.MmsSupport.java
com.zegoggles.smssync.mail.PersonLookup.java
com.zegoggles.smssync.mail.PersonRecord.java
com.zegoggles.smssync.preferences.AddressStyle.java
com.zegoggles.smssync.preferences.AuthMode.java
com.zegoggles.smssync.preferences.AuthPreferences.java
com.zegoggles.smssync.preferences.BackupManagerWrapper.java
com.zegoggles.smssync.preferences.CalendarPreferences.java
com.zegoggles.smssync.preferences.CallLogTypes.java
com.zegoggles.smssync.preferences.Defaults.java
com.zegoggles.smssync.preferences.MarkAsReadTypes.java
com.zegoggles.smssync.preferences.PreferenceBackupAgent.java
com.zegoggles.smssync.preferences.Preferences.java
com.zegoggles.smssync.preferences.ServerPreferences.java
com.zegoggles.smssync.receiver.BackupBroadcastReceiver.java
com.zegoggles.smssync.receiver.SmsBroadcastReceiver.java
com.zegoggles.smssync.service.Alarms.java
com.zegoggles.smssync.service.BackupConfig.java
com.zegoggles.smssync.service.BackupCursors.java
com.zegoggles.smssync.service.BackupItemsFetcher.java
com.zegoggles.smssync.service.BackupQueryBuilder.java
com.zegoggles.smssync.service.BackupTask.java
com.zegoggles.smssync.service.BackupType.java
com.zegoggles.smssync.service.BulkFetcher.java
com.zegoggles.smssync.service.CalendarSyncer.java
com.zegoggles.smssync.service.RestoreConfig.java
com.zegoggles.smssync.service.RestoreTask.java
com.zegoggles.smssync.service.ServiceBase.java
com.zegoggles.smssync.service.SmsBackupService.java
com.zegoggles.smssync.service.SmsRestoreService.java
com.zegoggles.smssync.service.UserCanceled.java
com.zegoggles.smssync.service.WhatsAppItemsFetcher.java
com.zegoggles.smssync.service.exception.BackupDisabledException.java
com.zegoggles.smssync.service.exception.ConnectivityException.java
com.zegoggles.smssync.service.exception.LocalizableException.java
com.zegoggles.smssync.service.exception.NoConnectionException.java
com.zegoggles.smssync.service.exception.RequiresBackgroundDataException.java
com.zegoggles.smssync.service.exception.RequiresLoginException.java
com.zegoggles.smssync.service.exception.RequiresWifiException.java
com.zegoggles.smssync.service.exception.SmsProviderNotWritableException.java
com.zegoggles.smssync.service.state.BackupState.java
com.zegoggles.smssync.service.state.RestoreState.java
com.zegoggles.smssync.service.state.SmsSyncState.java
com.zegoggles.smssync.service.state.State.java
com.zegoggles.smssync.tasks.OAuthCallbackTask.java
com.zegoggles.smssync.tasks.RequestTokenTask.java
com.zegoggles.smssync.utils.AppLog.java
com.zegoggles.smssync.utils.ListPreferenceHelper.java
com.zegoggles.smssync.utils.Sanitizer.java
com.zegoggles.smssync.utils.ThreadHelper.java