package org.dodgybits.shuffle.android.preference.activity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.dodgybits.android.shuffle.R;
import org.dodgybits.shuffle.android.core.activity.flurry.FlurryEnabledActivity;
import org.dodgybits.shuffle.android.core.model.Context;
import org.dodgybits.shuffle.android.core.model.Project;
import org.dodgybits.shuffle.android.core.model.Task;
import org.dodgybits.shuffle.android.core.model.persistence.EntityPersister;
import org.dodgybits.shuffle.android.core.model.protocol.ContextProtocolTranslator;
import org.dodgybits.shuffle.android.core.model.protocol.ProjectProtocolTranslator;
import org.dodgybits.shuffle.android.core.model.protocol.TaskProtocolTranslator;
import org.dodgybits.shuffle.android.core.view.AlertUtils;
import org.dodgybits.shuffle.android.persistence.provider.ContextProvider;
import org.dodgybits.shuffle.android.persistence.provider.ProjectProvider;
import org.dodgybits.shuffle.android.persistence.provider.TaskProvider;
import org.dodgybits.shuffle.android.preference.view.Progress;
import org.dodgybits.shuffle.dto.ShuffleProtos.Catalogue;
import org.dodgybits.shuffle.dto.ShuffleProtos.Catalogue.Builder;
import roboguice.inject.InjectView;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.google.inject.Inject;
public class PreferencesCreateBackupActivity extends FlurryEnabledActivity
implements View.OnClickListener {
private static final String CREATE_BACKUP_STATE = "createBackupState";
private static final String cTag = "PreferencesCreateBackupActivity";
private enum State {EDITING, IN_PROGRESS, COMPLETE, ERROR};
private State mState = State.EDITING;
@InjectView(R.id.filename) EditText mFilenameWidget;
@InjectView(R.id.saveButton) Button mSaveButton;
@InjectView(R.id.discardButton) Button mCancelButton;
@InjectView(R.id.progress_horizontal) ProgressBar mProgressBar;
@InjectView(R.id.progress_label) TextView mProgressText;
@Inject EntityPersister<Context> mContextPersister;
@Inject EntityPersister<Project> mProjectPersister;
@Inject EntityPersister<Task> mTaskPersister;
private AsyncTask<?, ?, ?> mTask;
@Override
protected void onCreate(Bundle icicle) {
Log.d(cTag, "onCreate+");
super.onCreate(icicle);
setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
setContentView(R.layout.backup_create);
findViewsAndAddListeners();
onUpdateState();
}
private void findViewsAndAddListeners() {
mSaveButton.setOnClickListener(this);
mCancelButton.setOnClickListener(this);
// save progress text when we switch orientation
mProgressText.setFreezesText(true);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.saveButton:
setState(State.IN_PROGRESS);
createBackup();
break;
case R.id.discardButton:
finish();
break;
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(CREATE_BACKUP_STATE, mState.name());
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String stateName = savedInstanceState.getString(CREATE_BACKUP_STATE);
if (stateName == null) {
stateName = State.EDITING.name();
}
setState(State.valueOf(stateName));
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mTask != null && mTask.getStatus() != AsyncTask.Status.RUNNING) {
mTask.cancel(true);
}
}
private void setState(State value) {
if (mState != value) {
mState = value;
onUpdateState();
}
}
private void onUpdateState() {
switch (mState) {
case EDITING:
setButtonsEnabled(true);
if (TextUtils.isEmpty(mFilenameWidget.getText())) {
Date today = new Date();
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
String defaultText = "shuffle-" + formatter.format(today) + ".bak";
mFilenameWidget.setText(defaultText);
mFilenameWidget.setSelection(0, defaultText.length() - 4);
}
mFilenameWidget.setEnabled(true);
mProgressBar.setVisibility(View.INVISIBLE);
mProgressText.setVisibility(View.INVISIBLE);
mCancelButton.setText(R.string.cancel_button_title);
break;
case IN_PROGRESS:
setButtonsEnabled(false);
mFilenameWidget.setSelection(0, 0);
mFilenameWidget.setEnabled(false);
mProgressBar.setProgress(0);
mProgressBar.setVisibility(View.VISIBLE);
mProgressText.setVisibility(View.VISIBLE);
break;
case COMPLETE:
setButtonsEnabled(true);
mFilenameWidget.setEnabled(false);
mProgressBar.setVisibility(View.VISIBLE);
mProgressText.setVisibility(View.VISIBLE);
mSaveButton.setVisibility(View.GONE);
mCancelButton.setText(R.string.ok_button_title);
break;
case ERROR:
setButtonsEnabled(true);
mFilenameWidget.setEnabled(true);
mProgressBar.setVisibility(View.VISIBLE);
mProgressText.setVisibility(View.VISIBLE);
mSaveButton.setVisibility(View.VISIBLE);
mCancelButton.setText(R.string.cancel_button_title);
break;
}
}
private void setButtonsEnabled(boolean enabled) {
mSaveButton.setEnabled(enabled);
mCancelButton.setEnabled(enabled);
}
private void createBackup() {
String filename = mFilenameWidget.getText().toString();
if (TextUtils.isEmpty(filename)) {
String message = getString(R.string.warning_filename_empty);
Log.e(cTag, message);
AlertUtils.showWarning(this, message);
setState(State.EDITING);
} else {
mTask = new CreateBackupTask().execute(filename);
}
}
private class CreateBackupTask extends AsyncTask<String, Progress, Void> {
public Void doInBackground(String... filename) {
try {
String message = getString(R.string.status_checking_media);
Log.d(cTag, message);
publishProgress(Progress.createProgress(0, message));
String storage_state = Environment.getExternalStorageState();
if (! Environment.MEDIA_MOUNTED.equals(storage_state)) {
message = getString(R.string.warning_media_not_mounted, storage_state);
reportError(message);
} else {
File dir = Environment.getExternalStorageDirectory();
final File backupFile = new File(dir, filename[0]);
message = getString(R.string.status_creating_backup);
Log.d(cTag, message);
publishProgress(Progress.createProgress(5, message));
if (backupFile.exists()) {
publishProgress(Progress.createErrorProgress("", new Runnable() {
@Override
public void run() {
showFileExistsWarning(backupFile);
}
}));
} else {
backupFile.createNewFile();
FileOutputStream out = new FileOutputStream(backupFile);
writeBackup(out);
}
}
} catch (Exception e) {
String message = getString(R.string.warning_backup_failed, e.getMessage());
reportError(message);
}
return null;
}
private void showFileExistsWarning(final File backupFile) {
OnClickListener buttonListener = new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON1) {
Log.i(cTag, "Overwriting file " + backupFile.getName());
try {
FileOutputStream out = new FileOutputStream(backupFile);
writeBackup(out);
} catch (Exception e) {
String message = getString(R.string.warning_backup_failed, e.getMessage());
reportError(message);
}
} else {
Log.d(cTag, "Hit Cancel button.");
setState(State.EDITING);
}
}
};
OnCancelListener cancelListener = new OnCancelListener() {
public void onCancel(DialogInterface dialog) {
Log.d(cTag, "Hit Cancel button.");
setState(State.EDITING);
}
};
AlertUtils.showFileExistsWarning(PreferencesCreateBackupActivity.this,
backupFile.getName(), buttonListener, cancelListener);
}
private void writeBackup(OutputStream out) throws IOException {
Builder builder = Catalogue.newBuilder();
writeContexts(builder, 10, 20);
writeProjects(builder, 20, 30);
writeTasks(builder, 30, 100);
builder.build().writeTo(out);
out.close();
String message = getString(R.string.status_backup_complete);
Progress progress = Progress.createProgress(100, message);
publishProgress(progress);
}
private void writeContexts(Builder builder, int progressStart, int progressEnd)
{
Log.d(cTag, "Writing contexts");
Cursor cursor = getContentResolver().query(
ContextProvider.Contexts.CONTENT_URI, ContextProvider.Contexts.cFullProjection,
null, null, null);
int i = 0;
int total = cursor.getCount();
String type = getString(R.string.context_name);
ContextProtocolTranslator translator = new ContextProtocolTranslator();
while (cursor.moveToNext()) {
Context context = mContextPersister.read(cursor);
builder.addContext(translator.toMessage(context));
String text = getString(R.string.backup_progress, type, context.getName());
int percent = calculatePercent(progressStart, progressEnd, ++i, total);
publishProgress(Progress.createProgress(percent, text));
}
cursor.close();
}
private void writeProjects(Builder builder, int progressStart, int progressEnd)
{
Log.d(cTag, "Writing projects");
Cursor cursor = getContentResolver().query(
ProjectProvider.Projects.CONTENT_URI, ProjectProvider.Projects.cFullProjection,
null, null, null);
int i = 0;
int total = cursor.getCount();
String type = getString(R.string.project_name);
ProjectProtocolTranslator translator = new ProjectProtocolTranslator(null);
while (cursor.moveToNext()) {
Project project = mProjectPersister.read(cursor);
builder.addProject(translator.toMessage(project));
String text = getString(R.string.backup_progress, type, project.getName());
int percent = calculatePercent(progressStart, progressEnd, ++i, total);
publishProgress(Progress.createProgress(percent, text));
}
cursor.close();
}
private void writeTasks(Builder builder, int progressStart, int progressEnd)
{
Log.d(cTag, "Writing tasks");
Cursor cursor = getContentResolver().query(
TaskProvider.Tasks.CONTENT_URI, TaskProvider.Tasks.cFullProjection,
null, null, null);
int i = 0;
int total = cursor.getCount();
String type = getString(R.string.task_name);
TaskProtocolTranslator translator = new TaskProtocolTranslator(null, null);
while (cursor.moveToNext()) {
Task task = mTaskPersister.read(cursor);
builder.addTask(translator.toMessage(task));
String text = getString(R.string.backup_progress, type, task.getDescription());
int percent = calculatePercent(progressStart, progressEnd, ++i, total);
publishProgress(Progress.createProgress(percent, text));
}
cursor.close();
}
private int calculatePercent(int start, int end, int current, int total) {
return start + (end - start) * current / total;
}
private void reportError(String message) {
Log.e(cTag, message);
publishProgress(Progress.createErrorProgress(message));
}
@Override
public void onProgressUpdate (Progress... progresses) {
Progress progress = progresses[0];
String details = progress.getDetails();
mProgressBar.setProgress(progress.getProgressPercent());
mProgressText.setText(details);
if (progress.isError()) {
if (!TextUtils.isEmpty(details)) {
AlertUtils.showWarning(PreferencesCreateBackupActivity.this, details);
}
Runnable action = progress.getErrorUIAction();
if (action != null) {
action.run();
} else {
setState(State.ERROR);
}
} else if (progress.isComplete()) {
setState(State.COMPLETE);
}
}
@SuppressWarnings("unused")
public void onPostExecute() {
mTask = null;
}
}
}