@Override public BackupState transition(SmsSyncState newState, Exception exception) { return new BackupState(newState, currentSyncedItems, itemsToSync, backupType, dataType, exception); }
private void notifyAboutBackup(BackupState state) { NotificationCompat.Builder builder = createNotification(R.string.status_backup); notification = builder.setContentTitle(getString(R.string.status_backup)) .setContentText(state.getNotificationLabel(getResources())) .setContentIntent(getPendingIntent(null)) .build(); startForeground(BACKUP_ID, notification); }
@Subscribe public void backupStateChanged(BackupState state) { if (this.state == state) return; this.state = state; if (this.state.isInitialState()) return; if (state.isError()) { handleErrorState(state); } if (state.isRunning()) { if (state.backupType == MANUAL) { notifyAboutBackup(state); } } else { appLogDebug(state.toString()); appLog(state.isCanceled() ? R.string.app_log_backup_canceled : R.string.app_log_backup_finished); scheduleNextBackup(state); stopForeground(true); stopSelf(); } }
@Subscribe public void backupStateChanged(final BackupState newState) { if ((newState.backupType == MANUAL || newState.backupType == SKIP) && newState.isPermissionException()) { ActivityCompat.requestPermissions(this, newState.getMissingPermissions(), newState.backupType == SKIP ? REQUEST_PERMISSIONS_BACKUP_MANUAL_SKIP : REQUEST_PERMISSIONS_BACKUP_MANUAL ); } }
@Subscribe public void backupStateChanged(BackupState state) { if (!state.isFinished()) { return; } final JobParameters jobParameters = jobs.remove(state.backupType.name()); if (jobParameters != null) { final boolean needsReschedule = state.isError() && !state.isPermissionException(); if (LOCAL_LOGV) { Log.v(TAG, "jobFinished(" + jobParameters + ", isError=" + state.isError() + ", needsReschedule="+needsReschedule+")"); } jobFinished(jobParameters, needsReschedule); } else { Log.w(TAG, "unknown job for state "+state); } }
private void handleErrorState(BackupState state) { if (state.isAuthException()) { appLog(R.string.app_log_backup_failed_authentication, state.getDetailedErrorMessage(getResources())); if (shouldNotifyUser(state)) { notifyUser(NOTIFICATION_ID_WARNING, notificationBuilder(stat_sys_warning, getString(R.string.notification_auth_failure), getString(getAuthPreferences().useXOAuth() ? R.string.status_auth_failure_details_xoauth : R.string.status_auth_failure_details_plain))); } } else if (state.isConnectivityError()) { appLog(R.string.app_log_backup_failed_connectivity, state.getDetailedErrorMessage(getResources())); } else if (state.isPermissionException()) { if (state.backupType != MANUAL) { Bundle extras = new Bundle(); extras.putStringArray(MainActivity.EXTRA_PERMISSIONS, state.getMissingPermissions()); notifyUser(NOTIFICATION_ID_WARNING, notificationBuilder(R.drawable.ic_notification, getString(R.string.notification_missing_permission), formatMissingPermissionDetails(getResources(), state.getMissingPermissions())) .setContentIntent(getPendingIntent(extras))); } } else { appLog(R.string.app_log_backup_failed_general_error, state.getDetailedErrorMessage(getResources())); if (shouldNotifyUser(state)) { notifyUser(NOTIFICATION_ID_WARNING, notificationBuilder(stat_sys_warning, getString(R.string.notification_general_error), state.getErrorMessage(getResources()))); } } }
@Test public void shouldGetErrorMessageRootCause() throws Exception { RuntimeException exception = new RuntimeException("foo", new IOException("bar")); BackupState state = new BackupState(SmsSyncState.ERROR, 0, 0, BackupType.REGULAR, DataType.SMS, exception); assertThat(state.getErrorMessage(resources)).isEqualTo("foo"); assertThat(state.getDetailedErrorMessage(resources)).isEqualTo("foo (exception: java.lang.RuntimeException: foo, underlying=java.io.IOException: bar)"); }
@Test public void shouldGetNotificationLabelCalculating() throws Exception { assertThat(new BackupState(SmsSyncState.CALC, 0, 0, BackupType.REGULAR, DataType.SMS, null).getNotificationLabel(resources)).isEqualTo( resources.getString(R.string.status_calc_details) ); }
@Test public void shouldGetErrorMessagePrefix() throws Exception { BackupState state = new BackupState(SmsSyncState.ERROR, 0, 0, BackupType.REGULAR, DataType.SMS, new MessagingException("Unable to get IMAP prefix")); assertThat(state.getErrorMessage(resources)).isEqualTo("Temporary Gmail IMAP error, try again later."); }
@Test public void shouldCheckError() throws Exception { assertThat(new BackupState(SmsSyncState.ERROR, 0, 0, BackupType.REGULAR, DataType.SMS, new IOException("foo") ).isError()).isTrue(); assertThat(new BackupState(SmsSyncState.FINISHED_BACKUP, 0, 0, BackupType.REGULAR, DataType.SMS, null ).isError()).isFalse(); }
private void backup(BackupType backupType) { getNotifier().cancel(NOTIFICATION_ID_WARNING); try { // set initial state state = new BackupState(INITIAL, 0, 0, backupType, null, null); EnumSet<DataType> enabledTypes = getEnabledBackupTypes(); checkPermissions(enabledTypes); if (backupType != SKIP) { checkCredentials(); if (getPreferences().isUseOldScheduler()) { legacyCheckConnectivity(); } } appLog(R.string.app_log_start_backup, backupType); getBackupTask().execute(getBackupConfig(backupType, enabledTypes, getBackupImapStore())); } catch (MessagingException e) { Log.w(TAG, e); moveToState(state.transition(ERROR, e)); } catch (ConnectivityException e) { moveToState(state.transition(ERROR, e)); } catch (RequiresLoginException e) { appLog(R.string.app_log_missing_credentials); moveToState(state.transition(ERROR, e)); } catch (BackupDisabledException e) { moveToState(state.transition(FINISHED_BACKUP, e)); } catch (MissingPermissionException e) { moveToState(state.transition(ERROR, e)); } }
@Test public void shouldSkipItems() throws Exception { when(fetcher.getMostRecentTimestamp(any(DataType.class))).thenReturn(-23L); BackupState finalState = task.doInBackground(new BackupConfig( store, 0, 100, new ContactGroup(-1), BackupType.SKIP, EnumSet.of(SMS), false ) ); verify(dataTypePreferences).setMaxSyncedDate(DataType.SMS, -23); verifyZeroInteractions(dataTypePreferences); assertThat(finalState).isNotNull(); assertThat(finalState.isFinished()).isTrue(); }
private boolean shouldNotifyUser(BackupState state) { return state.backupType == MANUAL || (getPreferences().isNotificationEnabled() && !state.isConnectivityError()); }
@Test public void shouldGetErrorMessage() throws Exception { BackupState state = new BackupState(SmsSyncState.ERROR, 0, 0, BackupType.REGULAR, DataType.SMS, new IOException("foo")); assertThat(state.getErrorMessage(resources)).isEqualTo("foo"); assertThat(state.getDetailedErrorMessage(resources)).isEqualTo("foo (exception: java.io.IOException: foo)"); }
@Test public void shouldGetNotificationLabelLogin() throws Exception { assertThat(new BackupState(SmsSyncState.LOGIN, 0, 0, BackupType.REGULAR, DataType.SMS, null).getNotificationLabel(resources)).isEqualTo( resources.getString(R.string.status_login_details) ); }
@Test public void shouldGetErrorMessageRequiresWifi() throws Exception { BackupState state = new BackupState(SmsSyncState.ERROR, 0, 0, BackupType.REGULAR, DataType.SMS, new RequiresWifiException()); assertThat(state.getErrorMessage(resources)).isEqualTo("No Wifi connection"); }
@Test public void shouldBackupItems() throws Exception { mockFetch(SMS, 1); when(converter.convertMessages(any(Cursor.class), eq(SMS))).thenReturn(result(SMS, 1)); when(store.getFolder(notNull(DataType.class), same(dataTypePreferences))).thenReturn(folder); BackupState finalState = task.doInBackground(config); verify(folder).appendMessages(anyListOf(Message.class)); verify(service).transition(SmsSyncState.LOGIN, null); verify(service).transition(SmsSyncState.CALC, null); assertThat(finalState).isNotNull(); assertThat(finalState.isFinished()).isTrue(); assertThat(finalState.currentSyncedItems).isEqualTo(1); assertThat(finalState.itemsToSync).isEqualTo(1); assertThat(finalState.backupType).isEqualTo(config.backupType); }
@Test public void shouldGetNotificationLabelError() throws Exception { assertThat(new BackupState(SmsSyncState.ERROR, 0, 0, BackupType.REGULAR, DataType.SMS, new IOException("foo")).getNotificationLabel(resources)) .isEqualTo("foo"); } }
private BackupState skip(Iterable<DataType> types) { appLog(R.string.app_log_skip_backup_skip_messages); for (DataType type : types) { try { preferences.getDataTypePreferences().setMaxSyncedDate(type, fetcher.getMostRecentTimestamp(type)); } catch (SecurityException e ) { return new BackupState(ERROR, 0, 0, MANUAL, type, e); } } Log.i(TAG, "All messages skipped."); return new BackupState(FINISHED_BACKUP, 0, 0, MANUAL, null, null); }
@Subscribe public void backupStateChanged(final BackupState newState) { if (App.LOCAL_LOGV) Log.v(TAG, "backupStateChanged:"+newState); if (newState.backupType.isBackground()) return; stateChanged(newState); switch (newState.state) { case FINISHED_BACKUP: finishedBackup(newState); break; case BACKUP: restoreButton.setEnabled(false); backupButton.setText(R.string.ui_sync_button_label_syncing); statusLabel.setText(R.string.status_backup); syncDetailsLabel.setText(newState.getNotificationLabel(getContext().getResources())); progressBar.setIndeterminate(false); progressBar.setProgress(newState.currentSyncedItems); progressBar.setMax(newState.itemsToSync); break; case CANCELED_BACKUP: statusLabel.setText(R.string.status_canceled); syncDetailsLabel.setText(getString(R.string.status_canceled_details, newState.currentSyncedItems, newState.itemsToSync)); break; } }