Java tutorial
package com.door43.translationstudio.ui.dialogs; import android.app.Activity; import android.app.Dialog; import android.app.DialogFragment; import android.app.Fragment; import android.app.FragmentTransaction; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.support.design.widget.Snackbar; import android.support.v4.content.FileProvider; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import org.unfoldingword.tools.logger.Logger; import com.door43.translationstudio.App; import com.door43.translationstudio.R; import com.door43.translationstudio.core.ExportUsfm; import com.door43.translationstudio.core.MergeConflictsHandler; import com.door43.translationstudio.core.TargetTranslation; import com.door43.translationstudio.core.TranslationViewMode; import com.door43.translationstudio.core.Translator; import com.door43.translationstudio.tasks.ExportProjectTask; import com.door43.translationstudio.tasks.ExportToUsfmTask; import com.door43.translationstudio.ui.ProfileActivity; import com.door43.translationstudio.ui.filechooser.FileChooserActivity; import com.door43.translationstudio.ui.translate.TargetTranslationActivity; import com.door43.translationstudio.tasks.CreateRepositoryTask; import com.door43.translationstudio.tasks.PullTargetTranslationTask; import com.door43.translationstudio.tasks.PushTargetTranslationTask; import com.door43.translationstudio.tasks.RegisterSSHKeysTask; import com.door43.util.SdUtils; import org.unfoldingword.tools.taskmanager.SimpleTaskWatcher; import org.unfoldingword.tools.taskmanager.ManagedTask; import org.unfoldingword.tools.taskmanager.TaskManager; import com.door43.widget.ViewUtil; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.ResetCommand; import org.eclipse.jgit.merge.MergeStrategy; import java.io.File; import java.security.InvalidParameterException; import org.unfoldingword.resourcecontainer.Project; /** * Created by joel on 10/5/2015. */ public class BackupDialog extends DialogFragment implements SimpleTaskWatcher.OnFinishedListener { public static final String TAG = BackupDialog.class.getName(); private static final int SELECT_EXPORT_FOLDER_REQUEST = 342; public static final String ARG_TARGET_TRANSLATION_ID = "target_translation_id"; private static final String STATE_SETTING_DEVICE_ALIAS = "state_setting_device_alias"; public static final String STATE_DIALOG_SHOWN = "state_dialog_shown"; public static final String STATE_DO_MERGE = "state_do_merge"; public static final String STATE_ACCESS_FILE = "state_access_file"; public static final String STATE_DIALOG_MESSAGE = "state_dialog_message"; public static final String STATE_OUTPUT_TO_DOCUMENT_FILE = "state_output_to_document_file"; public static final String STATE_OUTPUT_FOLDER_URI = "state_output_folder_uri"; public static final String EXTRA_OUTPUT_TO_USFM = "extra_output_to_usfm"; private TargetTranslation targetTranslation; private SimpleTaskWatcher taskWatcher; private boolean settingDeviceAlias = false; private LinearLayout mBackupToCloudButton = null; private eDialogShown mDialogShown = eDialogShown.NONE; private String mAccessFile; private String mDialogMessage; private boolean isOutputToDocumentFile; private Uri mDestinationFolderUri; private View v; @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = super.onCreateDialog(savedInstanceState); dialog.setCanceledOnTouchOutside(true); return dialog; } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { getDialog().requestWindowFeature(Window.FEATURE_NO_TITLE); v = inflater.inflate(R.layout.dialog_backup, container, false); // get target translation to backup Bundle args = getArguments(); if (args != null && args.containsKey(ARG_TARGET_TRANSLATION_ID)) { String targetTranslationId = args.getString(ARG_TARGET_TRANSLATION_ID, null); targetTranslation = App.getTranslator().getTargetTranslation(targetTranslationId); if (targetTranslation == null) { throw new InvalidParameterException( "The target translation '" + targetTranslationId + "' is invalid"); } } else { throw new InvalidParameterException("The target translation id was not specified"); } targetTranslation.setDefaultContributor(App.getProfile().getNativeSpeaker()); mBackupToCloudButton = (LinearLayout) v.findViewById(R.id.backup_to_cloud); LinearLayout exportProjectButton = (LinearLayout) v.findViewById(R.id.backup_to_sd); Button backupToAppButton = (Button) v.findViewById(R.id.backup_to_app); Button backupToDeviceButton = (Button) v.findViewById(R.id.backup_to_device); LinearLayout exportToPDFButton = (LinearLayout) v.findViewById(R.id.export_to_pdf); LinearLayout exportToUsfmButton = (LinearLayout) v.findViewById(R.id.export_to_usfm); Button logout = (Button) v.findViewById(R.id.logout_button); logout.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { App.setProfile(null); Intent logoutIntent = new Intent(getActivity(), ProfileActivity.class); startActivity(logoutIntent); } }); final String filename = targetTranslation.getId() + "." + Translator.ARCHIVE_EXTENSION; initProgressWatcher(R.string.backup); if (savedInstanceState != null) { // check if returning from device alias dialog settingDeviceAlias = savedInstanceState.getBoolean(STATE_SETTING_DEVICE_ALIAS, false); mDialogShown = eDialogShown .fromInt(savedInstanceState.getInt(STATE_DIALOG_SHOWN, eDialogShown.NONE.getValue())); mAccessFile = savedInstanceState.getString(STATE_ACCESS_FILE, null); mDialogMessage = savedInstanceState.getString(STATE_DIALOG_MESSAGE, null); isOutputToDocumentFile = savedInstanceState.getBoolean(STATE_OUTPUT_TO_DOCUMENT_FILE, false); mDestinationFolderUri = Uri.parse(savedInstanceState.getString(STATE_OUTPUT_FOLDER_URI, "")); restoreDialogs(); } Button dismissButton = (Button) v.findViewById(R.id.dismiss_button); dismissButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dismiss(); } }); backupToDeviceButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO: 11/18/2015 eventually we need to support bluetooth as well as an adhoc network showDeviceNetworkAliasDialog(); } }); // backup buttons mBackupToCloudButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (App.isNetworkAvailable()) { // make sure we have a gogs user if (App.getProfile().gogsUser == null) { showDoor43LoginDialog(); return; } doPullTargetTranslationTask(targetTranslation, MergeStrategy.RECURSIVE); } else { showNoInternetDialog(); // replaced snack popup which could be hidden behind dialog } } }); exportToPDFButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PrintDialog printDialog = new PrintDialog(); Bundle printArgs = new Bundle(); printArgs.putString(PrintDialog.ARG_TARGET_TRANSLATION_ID, targetTranslation.getId()); printDialog.setArguments(printArgs); showDialogFragment(printDialog, PrintDialog.TAG); } }); exportProjectButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { doSelectDestinationFolder(false); } }); exportToUsfmButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { doSelectDestinationFolder(true); } }); if (targetTranslation.isObsProject()) { LinearLayout exportToUsfmSeparator = (LinearLayout) v.findViewById(R.id.export_to_usfm_separator); exportToUsfmSeparator.setVisibility(View.GONE); exportToUsfmButton.setVisibility(View.GONE); } backupToAppButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { File exportFile = new File(App.getSharingDir(), filename); try { App.getTranslator().exportArchive(targetTranslation, exportFile); } catch (Exception e) { Logger.e(TAG, "Failed to export the target translation " + targetTranslation.getId(), e); } if (exportFile.exists()) { Uri u = FileProvider.getUriForFile(getActivity(), "com.door43.translationstudio.fileprovider", exportFile); Intent i = new Intent(Intent.ACTION_SEND); i.setType("application/zip"); i.putExtra(Intent.EXTRA_STREAM, u); startActivity(Intent.createChooser(i, "Email:")); } else { Snackbar snack = Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.translation_export_failed, Snackbar.LENGTH_LONG); ViewUtil.setSnackBarTextColor(snack, getResources().getColor(R.color.light_primary_text)); snack.show(); } } }); // connect to existing tasks PullTargetTranslationTask pullTask = (PullTargetTranslationTask) TaskManager .getTask(PullTargetTranslationTask.TASK_ID); RegisterSSHKeysTask keysTask = (RegisterSSHKeysTask) TaskManager.getTask(RegisterSSHKeysTask.TASK_ID); CreateRepositoryTask repoTask = (CreateRepositoryTask) TaskManager.getTask(CreateRepositoryTask.TASK_ID); PushTargetTranslationTask pushTask = (PushTargetTranslationTask) TaskManager .getTask(PushTargetTranslationTask.TASK_ID); ExportProjectTask projectExportTask = (ExportProjectTask) TaskManager.getTask(ExportProjectTask.TASK_ID); ExportToUsfmTask usfmExportTask = (ExportToUsfmTask) TaskManager.getTask(ExportToUsfmTask.TASK_ID); if (pullTask != null) { taskWatcher.watch(pullTask); } else if (keysTask != null) { taskWatcher.watch(keysTask); } else if (repoTask != null) { taskWatcher.watch(repoTask); } else if (pushTask != null) { taskWatcher.watch(pushTask); } else if (projectExportTask != null) { taskWatcher.watch(projectExportTask); } else if (usfmExportTask != null) { taskWatcher.watch(usfmExportTask); } return v; } /** * restore the dialogs that were displayed before rotation */ private void restoreDialogs() { Handler hand = new Handler(Looper.getMainLooper()); hand.post(new Runnable() { // wait for backup dialog to be drawn before showing popups @Override public void run() { switch (mDialogShown) { case PUSH_REJECTED: showPushRejection(targetTranslation); break; case AUTH_FAILURE: showAuthFailure(); break; case BACKUP_FAILED: showUploadFailedDialog(targetTranslation); break; case NO_INTERNET: showNoInternetDialog(); break; case SHOW_BACKUP_RESULTS: showBackupResults(mDialogMessage); break; case SHOW_PUSH_SUCCESS: showPushSuccess(mDialogMessage); break; case MERGE_CONFLICT: showMergeConflict(targetTranslation); break; case EXPORT_TO_USFM_PROMPT: showExportToUsfmPrompt(); break; case EXPORT_PROJECT_PROMPT: showExportProjectPrompt(); break; case EXPORT_TO_USFM_RESULTS: showUsfmExportResults(mDialogMessage); break; case EXPORT_PROJECT_OVERWRITE: showExportProjectOverwrite(mDialogMessage); break; case EXPORT_USFM_OVERWRITE: showExportUsfmOverwrite(mDialogMessage); break; case NONE: break; default: Logger.e(TAG, "Unsupported restore dialog: " + mDialogShown.toString()); break; } } }); } private void doSelectDestinationFolder(boolean usfmOutput) { String typeStr = null; Intent intent = new Intent(getActivity(), FileChooserActivity.class); isOutputToDocumentFile = SdUtils.isSdCardPresentLollipop(); if (isOutputToDocumentFile) { typeStr = FileChooserActivity.SD_CARD_TYPE; } else { typeStr = FileChooserActivity.INTERNAL_TYPE; } intent.setType(typeStr); Bundle args = new Bundle(); args.putString(FileChooserActivity.EXTRA_MODE, FileChooserActivity.SelectionMode.DIRECTORY.name()); args.putString(FileChooserActivity.EXTRA_TITLE, getActivity().getResources().getString(R.string.choose_destination_folder)); args.putBoolean(EXTRA_OUTPUT_TO_USFM, usfmOutput); intent.putExtras(args); startActivityForResult(intent, SELECT_EXPORT_FOLDER_REQUEST); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SELECT_EXPORT_FOLDER_REQUEST) { if (resultCode == Activity.RESULT_OK && data != null) { mDestinationFolderUri = data.getData(); isOutputToDocumentFile = !SdUtils.isRegularFile(this.mDestinationFolderUri); Bundle args = data.getExtras(); boolean usfmOutput = args.getBoolean(EXTRA_OUTPUT_TO_USFM, true); if (usfmOutput) { showExportToUsfmPrompt(); } else { showExportProjectPrompt(); } } } } /** * display confirmation prompt before USFM export (also allow entry of filename */ private void showExportToUsfmPrompt() { mDialogShown = eDialogShown.EXPORT_TO_USFM_PROMPT; ExportUsfm.BookData bookData = ExportUsfm.BookData.generate(targetTranslation); String defaultFileName = bookData.getDefaultUsfmFileName(); showExportPathPrompt(R.string.usfm_output_filename_title_prompt, defaultFileName); } /** * display confirmation prompt before USFM export (also allow entry of filename */ private void showExportProjectPrompt() { mDialogShown = eDialogShown.EXPORT_PROJECT_PROMPT; String filename = targetTranslation.getId() + "." + Translator.ARCHIVE_EXTENSION; showExportPathPrompt(R.string.project_output_filename_title_prompt, filename); } /** * display confirmation prompt before USFM export (also allow entry of filename * @param titleId */ private void showExportPathPrompt(int titleId, String defaultFileName) { final boolean isUsfmExport = (mDialogShown == eDialogShown.EXPORT_TO_USFM_PROMPT); LayoutInflater inflater = LayoutInflater.from(getActivity()); final View filenameFragment = inflater.inflate(R.layout.fragment_output_filename, null); if (filenameFragment != null) { final EditText filenameText = (EditText) filenameFragment.findViewById(R.id.filename_text); filenameText.setText(defaultFileName); if ((filenameText != null)) { new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(titleId) .setPositiveButton(R.string.label_continue, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; String destinationFilename = filenameText.getText().toString(); if (isUsfmExport) { boolean conflict = !saveToUsfm(targetTranslation, destinationFilename, false); if (conflict) { showExportUsfmOverwrite(destinationFilename); } } else { boolean conflict = !saveProjectFile(targetTranslation, destinationFilename, false); if (conflict) { showExportProjectOverwrite(destinationFilename); } } } }).setNeutralButton(R.string.dismiss, null).setView(filenameFragment).show(); } } } /** * display confirmation prompt before USFM export (also allow entry of filename) * @param fileName */ private void showExportUsfmOverwrite(final String fileName) { mDialogShown = eDialogShown.EXPORT_USFM_OVERWRITE; mDialogMessage = fileName; String message = getOverwriteMessage(mDestinationFolderUri, fileName); new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.overwrite_file_title) .setMessage(message) .setPositiveButton(R.string.overwrite_label, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; saveToUsfm(targetTranslation, fileName, true); } }).setNeutralButton(R.string.title_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; dialog.dismiss(); } }).setNegativeButton(R.string.rename_label, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.EXPORT_TO_USFM_PROMPT; showExportPathPrompt(R.string.usfm_output_filename_title_prompt, fileName); } }).show(); } /** * format a message for overwrite warning * @param fileName * @return */ private String getOverwriteMessage(Uri uri, String fileName) { String path = SdUtils.getPathString(uri, fileName); String sizeStr = SdUtils.getFormattedFileSize(getActivity(), uri, fileName); String dateStr = SdUtils.getDate(getActivity(), uri, fileName); return getString(R.string.overwrite_file_warning, path, sizeStr, dateStr); } /** * display confirmation prompt before Project export (also allow entry of filename) * @param fileName */ private void showExportProjectOverwrite(final String fileName) { mDialogShown = eDialogShown.EXPORT_PROJECT_OVERWRITE; mDialogMessage = fileName; String message = getOverwriteMessage(mDestinationFolderUri, fileName); new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.overwrite_file_title) .setMessage(message) .setPositiveButton(R.string.overwrite_label, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; saveProjectFile(targetTranslation, fileName, true); } }).setNeutralButton(R.string.title_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; dialog.dismiss(); } }).setNegativeButton(R.string.rename_label, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.EXPORT_PROJECT_PROMPT; showExportPathPrompt(R.string.project_output_filename_title_prompt, fileName); } }).show(); } /** * save to usfm file and give success notification * @param targetTranslation * @param force - if true then we will overwrite existing file * @return false if not forced and file already is present */ private boolean saveToUsfm(TargetTranslation targetTranslation, String fileName, boolean force) { if (!force && SdUtils.exists(mDestinationFolderUri, fileName)) { return false; } initProgressWatcher(R.string.exporting); ExportToUsfmTask usfmExportTask = new ExportToUsfmTask(getActivity(), targetTranslation, mDestinationFolderUri, fileName, isOutputToDocumentFile); taskWatcher.watch(usfmExportTask); TaskManager.addTask(usfmExportTask, ExportToUsfmTask.TASK_ID); return true; } /** * show USFM export results * @param message */ private void showUsfmExportResults(String message) { mDialogShown = eDialogShown.EXPORT_TO_USFM_RESULTS; mDialogMessage = message; new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.title_export_usfm) .setMessage(message).setPositiveButton(R.string.dismiss, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; } }).show(); } private void showDoor43LoginDialog() { Door43LoginDialog dialog = new Door43LoginDialog(); showDialogFragment(dialog, Door43LoginDialog.TAG); } private void showDeviceNetworkAliasDialog() { if (App.isNetworkAvailable()) { if (App.getDeviceNetworkAlias() == null) { showDeviceNetworkAliasDialogSub(); } else { showP2PDialog(); } } else { Snackbar snack = Snackbar.make(getActivity().findViewById(android.R.id.content), R.string.internet_not_available, Snackbar.LENGTH_LONG); ViewUtil.setSnackBarTextColor(snack, getResources().getColor(R.color.light_primary_text)); snack.show(); } } private void showDeviceNetworkAliasDialogSub() { settingDeviceAlias = true; DeviceNetworkAliasDialog dialog = new DeviceNetworkAliasDialog(); showDialogFragment(dialog, "device-name-dialog"); } /** * back up project - will try to write to user selected destination * @param filename * @param force - if true then we will overwrite existing file * @return false if not forced and file already is present */ private boolean saveProjectFile(TargetTranslation targetTranslation, String filename, boolean force) { if (!force && SdUtils.exists(mDestinationFolderUri, filename)) { return false; } initProgressWatcher(R.string.exporting); ExportProjectTask sdExportTask = new ExportProjectTask(getActivity(), filename, mDestinationFolderUri, targetTranslation); taskWatcher.watch(sdExportTask); TaskManager.addTask(sdExportTask, ExportProjectTask.TASK_ID); return true; } /** * creates a new progress watcher with desired title * @param titleID */ private void initProgressWatcher(int titleID) { if (taskWatcher != null) { taskWatcher.stop(); } taskWatcher = new SimpleTaskWatcher(getActivity(), titleID); taskWatcher.setOnFinishedListener(this); } private void showBackupResults(final int textResId, final String filePath) { String message = getResources().getString(textResId); if (filePath != null) { message += "\n" + filePath; } showBackupResults(message); } private void showBackupResults(String message) { mDialogShown = eDialogShown.SHOW_BACKUP_RESULTS; mDialogMessage = message; new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.backup_to_sd) .setMessage(message).setNeutralButton(R.string.dismiss, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; } }).show(); } @Override public void onResume() { if (settingDeviceAlias && App.getDeviceNetworkAlias() != null) { settingDeviceAlias = false; showP2PDialog(); } TextView currentUser = (TextView) v.findViewById(R.id.current_user); String userText = getResources().getString(R.string.current_user, ProfileActivity.getCurrentUser()); currentUser.setText(userText); super.onResume(); } /** * Displays the dialog for p2p sharing */ private void showP2PDialog() { ShareWithPeerDialog dialog = new ShareWithPeerDialog(); Bundle args = new Bundle(); args.putInt(ShareWithPeerDialog.ARG_OPERATION_MODE, ShareWithPeerDialog.MODE_SERVER); args.putString(ShareWithPeerDialog.ARG_TARGET_TRANSLATION, targetTranslation.getId()); args.putString(ShareWithPeerDialog.ARG_DEVICE_ALIAS, App.getDeviceNetworkAlias()); dialog.setArguments(args); showDialogFragment(dialog, "share-dialog"); } /** * this is to fix old method which when called in onResume() would create a * second dialog overlaying the first. The first was actually not removed. * Doing a commit after the remove() and starting a second FragmentTransaction * seems to fix the duplicate dialog bug. * * @param dialog * @param tag */ private void showDialogFragment(android.app.DialogFragment dialog, String tag) { FragmentTransaction backupFt = getFragmentManager().beginTransaction(); Fragment backupPrev = getFragmentManager().findFragmentByTag(tag); if (backupPrev != null) { backupFt.remove(backupPrev); backupFt.commit(); // apply the remove backupFt = getFragmentManager().beginTransaction(); // start a new transaction } backupFt.addToBackStack(null); dialog.show(backupFt, tag); } @Override public void onFinished(ManagedTask task) { taskWatcher.stop(); if (task instanceof PullTargetTranslationTask) { PullTargetTranslationTask pullTask = (PullTargetTranslationTask) task; // mRemoteURL = pullTask.getSourceURL(); PullTargetTranslationTask.Status status = pullTask.getStatus(); // TRICKY: we continue to push for unknown status in case the repo was just created (the missing branch is an error) // the pull task will catch any errors if (status == PullTargetTranslationTask.Status.UP_TO_DATE || status == PullTargetTranslationTask.Status.UNKNOWN) { Logger.i(this.getClass().getName(), "Changes on the server were synced with " + targetTranslation.getId()); initProgressWatcher(R.string.backup); PushTargetTranslationTask pushtask = new PushTargetTranslationTask(targetTranslation); taskWatcher.watch(pushtask); TaskManager.addTask(pushtask, PushTargetTranslationTask.TASK_ID); } else if (status == PullTargetTranslationTask.Status.AUTH_FAILURE) { Logger.i(this.getClass().getName(), "Authentication failed"); // if we have already tried ask the user if they would like to try again if (App.hasSSHKeys()) { showAuthFailure(); return; } initProgressWatcher(R.string.backup); RegisterSSHKeysTask keyTask = new RegisterSSHKeysTask(false); taskWatcher.watch(keyTask); TaskManager.addTask(keyTask, RegisterSSHKeysTask.TASK_ID); } else if (status == PullTargetTranslationTask.Status.NO_REMOTE_REPO) { Logger.i(this.getClass().getName(), "The repository " + targetTranslation.getId() + " could not be found"); // create missing repo initProgressWatcher(R.string.backup); CreateRepositoryTask repoTask = new CreateRepositoryTask(targetTranslation); taskWatcher.watch(repoTask); TaskManager.addTask(repoTask, CreateRepositoryTask.TASK_ID); } else if (status == PullTargetTranslationTask.Status.MERGE_CONFLICTS) { Logger.i(this.getClass().getName(), "The server contains conflicting changes for " + targetTranslation.getId()); MergeConflictsHandler.backgroundTestForConflictedChunks(targetTranslation.getId(), new MergeConflictsHandler.OnMergeConflictListener() { @Override public void onNoMergeConflict(String targetTranslationId) { // probably the manifest or license gave a false positive Logger.i(this.getClass().getName(), "Changes on the server were synced with " + targetTranslation.getId()); initProgressWatcher(R.string.backup); PushTargetTranslationTask pushtask = new PushTargetTranslationTask( targetTranslation); taskWatcher.watch(pushtask); TaskManager.addTask(pushtask, PushTargetTranslationTask.TASK_ID); } @Override public void onMergeConflict(String targetTranslationId) { showMergeConflict(targetTranslation); } }); } else { notifyBackupFailed(targetTranslation); } } else if (task instanceof RegisterSSHKeysTask) { if (((RegisterSSHKeysTask) task).isSuccess()) { Logger.i(this.getClass().getName(), "SSH keys were registered with the server"); // try to push again doPullTargetTranslationTask(targetTranslation, MergeStrategy.RECURSIVE); } else { notifyBackupFailed(targetTranslation); } } else if (task instanceof CreateRepositoryTask) { if (((CreateRepositoryTask) task).isSuccess()) { Logger.i(this.getClass().getName(), "A new repository " + targetTranslation.getId() + " was created on the server"); doPullTargetTranslationTask(targetTranslation, MergeStrategy.RECURSIVE); } else { notifyBackupFailed(targetTranslation); } } else if (task instanceof PushTargetTranslationTask) { PushTargetTranslationTask.Status status = ((PushTargetTranslationTask) task).getStatus(); final String message = ((PushTargetTranslationTask) task).getMessage(); if (status == PushTargetTranslationTask.Status.OK) { Logger.i(this.getClass().getName(), "The target translation " + targetTranslation.getId() + " was pushed to the server"); showPushSuccess(message); } else if (status.isRejected()) { Logger.i(this.getClass().getName(), "Push Rejected"); showPushRejection(targetTranslation); } else if (status == PushTargetTranslationTask.Status.AUTH_FAILURE) { Logger.i(this.getClass().getName(), "Authentication failed"); showAuthFailure(); } else { notifyBackupFailed(targetTranslation); } } else if (task instanceof ExportProjectTask) { ExportProjectTask projectExportTask = (ExportProjectTask) task; ExportProjectTask.ExportResults results = (ExportProjectTask.ExportResults) projectExportTask .getResult(); Logger.i(TAG, "Project export success = " + results.success); if (results.success) { showBackupResults(R.string.backup_success, results.filePath); } else { showBackupResults(R.string.backup_failed, results.filePath); } } else if (task instanceof ExportToUsfmTask) { ExportToUsfmTask usfmExportTask = (ExportToUsfmTask) task; Uri exportFile = (Uri) usfmExportTask.getResult(); boolean success = (exportFile != null); String message; if (success) { String format = getResources().getString(R.string.export_success); message = String.format(format, SdUtils.getPathString(exportFile.toString())); } else { message = getResources().getString(R.string.export_failed); } showUsfmExportResults(message); } } private void showPushSuccess(final String message) { mDialogShown = eDialogShown.SHOW_PUSH_SUCCESS; mDialogMessage = message; final Uri url = Uri.parse("https://door43.org/u/" + App.getProfile().gogsUser.getUsername() + "/" + targetTranslation.getId()); new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.upload_complete) .setMessage(String.format(getResources().getString(R.string.project_uploaded_to), url.toString())) .setNegativeButton(R.string.dismiss, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; } }).setPositiveButton(R.string.view_online, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startActivity(new Intent(Intent.ACTION_VIEW, url)); } }).setNeutralButton(R.string.label_details, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog) .setTitle(R.string.project_uploaded).setMessage(message) .setPositiveButton(R.string.view_online, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startActivity(new Intent(Intent.ACTION_VIEW, url)); } }).setNeutralButton(R.string.dismiss, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; } }).show(); } }).show(); } /** * let user know there was a merge conflict * @param targetTranslation */ private void showMergeConflict(final TargetTranslation targetTranslation) { mDialogShown = eDialogShown.MERGE_CONFLICT; String projectID = targetTranslation.getProjectId(); String message = String.format(getResources().getString(R.string.merge_request), projectID, targetTranslation.getTargetLanguageName()); new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.change_detected) .setMessage(message).setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; doManualMerge(); } }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; resetToMasterBackup(targetTranslation); BackupDialog.this.dismiss(); } }).show(); } /** * open review mode to let user resolve conflict */ private void doManualMerge() { if (getActivity() instanceof TargetTranslationActivity) { ((TargetTranslationActivity) getActivity()).redrawTarget(); BackupDialog.this.dismiss(); // TODO: 4/20/16 it would be nice to navigate directly to the first conflict } else { // ask parent activity to navigate to a new activity Intent intent = new Intent(getActivity(), TargetTranslationActivity.class); Bundle args = new Bundle(); args.putString(App.EXTRA_TARGET_TRANSLATION_ID, targetTranslation.getId()); // TODO: 4/20/16 it would be nice to navigate directly to the first conflict // args.putString(App.EXTRA_CHAPTER_ID, chapterId); // args.putString(App.EXTRA_FRAME_ID, frameId); args.putInt(App.EXTRA_VIEW_MODE, TranslationViewMode.REVIEW.ordinal()); intent.putExtras(args); startActivity(intent); BackupDialog.this.dismiss(); } } private void doPullTargetTranslationTask(TargetTranslation targetTranslation, MergeStrategy theirs) { initProgressWatcher(R.string.backup); PullTargetTranslationTask pullTask = new PullTargetTranslationTask(targetTranslation, theirs, null); taskWatcher.watch(pullTask); TaskManager.addTask(pullTask, PullTargetTranslationTask.TASK_ID); } /** * Displays a dialog to the user indicating the publish failed. * Includes an option to submit a bug report * @param targetTranslation */ private void notifyBackupFailed(final TargetTranslation targetTranslation) { if (!App.isNetworkAvailable()) { showNoInternetDialog(); } else { showUploadFailedDialog(targetTranslation); } } /** * show internet not available dialog */ private void showNoInternetDialog() { mDialogShown = eDialogShown.NO_INTERNET; new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.upload_failed) .setMessage(R.string.internet_not_available) .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; } }).show(); } /** * show general upload failed dialog * @param targetTranslation */ private void showUploadFailedDialog(final TargetTranslation targetTranslation) { mDialogShown = eDialogShown.BACKUP_FAILED; new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.backup) .setMessage(R.string.upload_failed) .setPositiveButton(R.string.dismiss, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; } }).setNeutralButton(R.string.menu_bug, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; showFeedbackDialog(targetTranslation); } }).show(); } private void showFeedbackDialog(TargetTranslation targetTranslation) { Project project = App.getLibrary().index().getProject("en", targetTranslation.getProjectId(), true); // open bug report dialog FeedbackDialog feedbackDialog = new FeedbackDialog(); Bundle args = new Bundle(); String message = "Failed to upload the translation of " + project.name + " into " + targetTranslation.getTargetLanguageName() + ".\ntargetTranslation: " + targetTranslation.getId() + "\n--------\n\n"; args.putString(FeedbackDialog.ARG_MESSAGE, message); feedbackDialog.setArguments(args); showDialogFragment(feedbackDialog, "feedback-dialog"); } public void showPushRejection(final TargetTranslation targetTranslation) { mDialogShown = eDialogShown.PUSH_REJECTED; new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.upload_failed) .setMessage(R.string.push_rejected) .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; doManualMerge(); } }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; resetToMasterBackup(targetTranslation); BackupDialog.this.dismiss(); } }).show(); } private boolean resetToMasterBackup(TargetTranslation targetTranslation) { try { // restore state before the pull Git git = targetTranslation.getRepo().getGit(); ResetCommand resetCommand = git.reset(); resetCommand.setMode(ResetCommand.ResetType.HARD).setRef("backup-master").call(); } catch (Exception e) { e.printStackTrace(); return false; } return true; } public void showAuthFailure() { mDialogShown = eDialogShown.AUTH_FAILURE; new AlertDialog.Builder(getActivity(), R.style.AppTheme_Dialog).setTitle(R.string.upload_failed) .setMessage(R.string.auth_failure_retry) .setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; initProgressWatcher(R.string.backup); RegisterSSHKeysTask keyTask = new RegisterSSHKeysTask(true); taskWatcher.watch(keyTask); TaskManager.addTask(keyTask, RegisterSSHKeysTask.TASK_ID); } }).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mDialogShown = eDialogShown.NONE; notifyBackupFailed(targetTranslation); } }).show(); } @Override public void onSaveInstanceState(Bundle out) { // remember if the device alias dialog is open out.putBoolean(STATE_SETTING_DEVICE_ALIAS, settingDeviceAlias); out.putInt(STATE_DO_MERGE, mDialogShown.getValue()); if (mAccessFile != null) { out.putString(STATE_ACCESS_FILE, mAccessFile); } out.putInt(STATE_DIALOG_SHOWN, mDialogShown.getValue()); if (mDialogMessage != null) { out.putString(STATE_DIALOG_MESSAGE, mDialogMessage); } out.putBoolean(STATE_OUTPUT_TO_DOCUMENT_FILE, isOutputToDocumentFile); if (mDestinationFolderUri != null) { out.putString(STATE_OUTPUT_FOLDER_URI, mDestinationFolderUri.toString()); } super.onSaveInstanceState(out); } @Override public void onDestroy() { if (taskWatcher != null) { taskWatcher.stop(); } super.onDestroy(); } /** * for keeping track which dialog is being shown for orientation changes (not for DialogFragments) */ public enum eDialogShown { NONE(0), PUSH_REJECTED(1), AUTH_FAILURE(2), BACKUP_FAILED(3), EXPORT_PROJECT_PROMPT(4), SHOW_BACKUP_RESULTS( 5), SHOW_PUSH_SUCCESS(6), MERGE_CONFLICT(7), EXPORT_TO_USFM_PROMPT(8), EXPORT_TO_USFM_RESULTS( 9), NO_INTERNET(10), EXPORT_PROJECT_OVERWRITE(11), EXPORT_USFM_OVERWRITE(12); private int value; eDialogShown(int Value) { this.value = Value; } public int getValue() { return value; } public static eDialogShown fromInt(int i) { for (eDialogShown b : eDialogShown.values()) { if (b.getValue() == i) { return b; } } return null; } } }