Activity with progress and summary for import all option. Fixes #471.

ErrorListDialog shared by ExportActivity and ImportActivity.
This commit is contained in:
Román Martínez
2020-10-23 18:57:37 +02:00
parent 48c3695b5d
commit acc2627bde
19 changed files with 701 additions and 445 deletions
@@ -0,0 +1,67 @@
package de.dennisguse.opentracks.io.file;
import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import java.util.ArrayList;
import de.dennisguse.opentracks.R;
public class ErrorListDialog extends DialogFragment {
public static final String TAG = ErrorListDialog.class.getSimpleName();
private static final String EXTRA_TITLE = "extra_title";
private static final String EXTRA_ERROR_LIST = "extra_error_list";
private ArrayList<String> errorList;
private String title;
public static void showDialog(FragmentManager fragmentManager, String title, ArrayList<String> errorList) {
Bundle bundle = new Bundle();
bundle.putString(EXTRA_TITLE, title);
bundle.putStringArrayList(EXTRA_ERROR_LIST, errorList);
ErrorListDialog errorListDialog = new ErrorListDialog();
errorListDialog.setArguments(bundle);
errorListDialog.setRetainInstance(true);
errorListDialog.show(fragmentManager, TAG);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
title = getArguments().getString(EXTRA_TITLE);
errorList = getArguments().getStringArrayList(EXTRA_ERROR_LIST);
} else {
title = savedInstanceState.getString(EXTRA_TITLE);
errorList = savedInstanceState.getStringArrayList(EXTRA_ERROR_LIST);
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString(EXTRA_TITLE, title);
outState.putStringArrayList(EXTRA_ERROR_LIST, errorList);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final String[] tracks = errorList.stream().toArray(String[]::new);
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity())
.setTitle(title)
.setItems(tracks, null)
.setPositiveButton(R.string.generic_ok, (dialog, which) -> dismiss());
AlertDialog dialog = alertDialogBuilder.create();
return dialog;
}
}
@@ -16,8 +16,6 @@
package de.dennisguse.opentracks.io.file.exporter;
import android.app.AlertDialog;
import android.app.Dialog;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
@@ -34,7 +32,6 @@ import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
import java.util.ArrayList;
@@ -45,6 +42,7 @@ import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.content.data.TracksColumns;
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
import de.dennisguse.opentracks.io.file.ErrorListDialog;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.ExportUtils;
import de.dennisguse.opentracks.util.FileUtils;
@@ -68,6 +66,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
private static final String BUNDLE_SKIPPED_COUNT = "track_export_skipped_count";
private static final String BUNDLE_TOTAL_COUNT = "track_export_total_count";
private static final String BUNDLE_DIRECTORY_FILES = "track_directory_files";
private static final String BUNDLE_TRACK_ERRORS = "track_errors";
private static final int CONFLICT_NONE = 0;
private static final int CONFLICT_OVERWRITE = 1;
@@ -99,6 +98,8 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
private Button viewLeftButton;
private Button viewRightButton;
private ArrayList<String> trackErrors = new ArrayList<>();
private int autoConflict;
private ContentProviderUtils contentProviderUtils;
@@ -159,8 +160,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
Toolbar toolbar = findViewById(R.id.toolbar);
toolbar.setTitle(getString(R.string.export_progress_message, directoryDisplayName));
resultReceiver = new ExportServiceResultReceiver(new Handler());
resultReceiver.setReceiver(this);
resultReceiver = new ExportServiceResultReceiver(new Handler(), this);
viewTotal = findViewById(R.id.export_progress_total);
viewDone = findViewById(R.id.export_progress_done);
@@ -187,6 +187,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
trackExportSkippedCount = savedInstanceState.getInt(BUNDLE_SKIPPED_COUNT);
trackExportTotalCount = savedInstanceState.getInt(BUNDLE_TOTAL_COUNT);
directoryFiles = savedInstanceState.getStringArrayList(BUNDLE_DIRECTORY_FILES);
trackErrors = savedInstanceState.getStringArrayList(BUNDLE_TRACK_ERRORS);
setProgress();
initExport(getTotalDone());
@@ -203,6 +204,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
outState.putInt(BUNDLE_SKIPPED_COUNT, trackExportSkippedCount);
outState.putInt(BUNDLE_TOTAL_COUNT, trackExportTotalCount);
outState.putStringArrayList(BUNDLE_DIRECTORY_FILES, (ArrayList<String>) directoryFiles);
outState.putStringArrayList(BUNDLE_TRACK_ERRORS, trackErrors);
}
@Override
@@ -220,7 +222,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
}
this.doubleBackToCancel = true;
Toast.makeText(this, getString(R.string.export_click_twice_cancel), Toast.LENGTH_SHORT).show();
Toast.makeText(this, getString(R.string.generic_click_twice_cancel), Toast.LENGTH_SHORT).show();
new Handler().postDelayed(() -> doubleBackToCancel=false, 2000);
}
@@ -311,15 +313,15 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
if (trackExportErrorCount > 0) {
viewLeftButton.setVisibility(View.VISIBLE);
viewLeftButton.setText(getString(R.string.export_show_errors));
viewLeftButton.setOnClickListener((view) -> new ErrorListDialog().show(getSupportFragmentManager(), ErrorListDialog.TAG));
viewLeftButton.setText(getString(R.string.generic_show_errors));
viewLeftButton.setOnClickListener((view) -> ErrorListDialog.showDialog(getSupportFragmentManager(), getString(R.string.export_track_errors), trackErrors));
viewAlertIcon.setImageDrawable(getDrawable(R.drawable.ic_report_problem_24));
String msg = getResources().getQuantityString(R.plurals.export_all_end_with_errors, trackExportErrorCount, trackExportErrorCount);
String msg = getResources().getQuantityString(R.plurals.generic_completed_with_errors, trackExportErrorCount, trackExportErrorCount);
viewAlertMsg.setText(msg);
} else {
viewLeftButton.setVisibility(View.GONE);
viewAlertIcon.setImageDrawable(getDrawable(R.drawable.ic_dialog_success_24dp));
viewAlertMsg.setText(getString(R.string.export_all_end));
viewAlertMsg.setText(getString(R.string.generic_completed));
}
viewRightButton.setOnClickListener((view) -> finish());
@@ -337,7 +339,7 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
case ExportServiceResultReceiver.RESULT_CODE_ERROR:
trackExportErrorCount++;
Track track = contentProviderUtils.getTrack(trackId);
ErrorListDialog.trackErrors.add(track.getName());
trackErrors.add(track.getName());
break;
case ExportServiceResultReceiver.RESULT_CODE_SUCCESS:
if (ExportUtils.isExportFileExists(trackId, trackFileFormat.getExtension(), directoryFiles)) {
@@ -413,22 +415,4 @@ public class ExportActivity extends FragmentActivity implements ExportServiceRes
}
}
}
public static class ErrorListDialog extends DialogFragment {
public static final String TAG = ErrorListDialog.class.getSimpleName();
public static List<String> trackErrors = new ArrayList<>();
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final String[] tracks = trackErrors.stream().toArray(String[]::new);
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getActivity())
.setTitle(getString(R.string.export_track_errors))
.setItems(tracks, null)
.setPositiveButton(R.string.generic_ok, (dialog, which) -> dismiss());
AlertDialog dialog = alertDialogBuilder.create();
return dialog;
}
}
}
@@ -17,8 +17,9 @@ public class ExportServiceResultReceiver extends ResultReceiver {
private Receiver receiver;
public ExportServiceResultReceiver(Handler handler) {
public ExportServiceResultReceiver(Handler handler, Receiver receiver) {
super(handler);
this.receiver = receiver;
}
@Override
@@ -28,10 +29,6 @@ public class ExportServiceResultReceiver extends ResultReceiver {
}
}
public void setReceiver(Receiver receiver) {
this.receiver = receiver;
}
public interface Receiver {
void onReceiveResult(int resultCode, Bundle resultData);
}
@@ -134,13 +134,18 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
saxParser.parse(inputStream, this);
Log.d(TAG, "Total import time: " + (System.currentTimeMillis() - start) + "ms");
if (trackIds.size() != 1) {
throw new MultiTracksImportException();
// TODO Multi track is not supported yet.
throw new ImportParserException("Multi track not supported");
}
return trackIds.get(0);
} catch (MultiTracksImportException | IOException | SAXException | SQLiteConstraintException | ParserConfigurationException e) {
} catch (IOException | SAXException | ParserConfigurationException e) {
Log.e(TAG, "Unable to import file", e);
cleanImport();
return null;
throw new ImportParserException(e);
} catch (SQLiteConstraintException e) {
Log.e(TAG, "Unable to import file", e);
cleanImport();
throw new ImportAlreadyExistsException(e);
}
}
@@ -550,9 +555,4 @@ abstract class AbstractFileTrackImporter extends DefaultHandler implements Track
// The number of buffered locations
int numBufferedTrackPoints = 0;
}
@Deprecated
//TODO According to the reader AND writer, we support multiple tracks per file; we should implement it here also.
private static class MultiTracksImportException extends RuntimeException {
}
}
@@ -16,62 +16,178 @@
package de.dennisguse.opentracks.io.file.importer;
import android.content.ClipData;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.documentfile.provider.DocumentFile;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModelProvider;
import de.dennisguse.opentracks.fragments.ImportFinishedDialogFragment;
import de.dennisguse.opentracks.fragments.ImportProgressDialogFragment;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.io.file.ErrorListDialog;
/**
* An activity to import files from the external storage.
*
* @author Rodrigo Damazio
*/
public class ImportActivity extends FragmentActivity implements ImportProgressDialogFragment.DismissCallback, ImportFinishedDialogFragment.DismissCallback {
public class ImportActivity extends FragmentActivity {
private static final String TAG = ImportActivity.class.getSimpleName();
public static final String EXTRA_DIRECTORY_URI_KEY = "directory_uri";
private static final String BUNDLE_ACTIVITY_RECREATED = "activity_recreated";
private static final String BUNDLE_TOOLBAR_TITLE = "toolbar_title";
private static final String BUNDLE_DOCUMENT_URI = "document_uri";
private static final String BUNDLE_IS_DIRECTORY = "is_directory";
private TextView viewTotal;
private TextView viewDone;
private TextView viewSummary;
private ProgressBar viewProgressBar;
private ImageView viewAlertIcon;
private TextView viewAlertMsg;
private Button viewLeftButton;
private Button viewRightButton;
boolean doubleBackToCancel = false;
private Uri documentUri;
private boolean isDirectory;
private String toolbarTitle;
private ImportViewModel viewModel;
private ImportViewModel.Summary summary;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.import_progress_activity);
if (savedInstanceState == null || !savedInstanceState.getBoolean(BUNDLE_ACTIVITY_RECREATED, false)) {
Toolbar toolbar = findViewById(R.id.toolbar);
viewTotal = findViewById(R.id.import_progress_total);
viewDone = findViewById(R.id.import_progress_done);
viewSummary = findViewById(R.id.import_progress_summary);
viewProgressBar = findViewById(R.id.import_progress_bar);
viewAlertIcon = findViewById(R.id.import_progress_alert_icon);
viewAlertMsg = findViewById(R.id.import_progress_alert_msg);
viewLeftButton = findViewById(R.id.import_progress_left_button);
viewRightButton = findViewById(R.id.import_progress_right_button);
final DocumentFile documentFile;
if (savedInstanceState == null) {
if (getIntent().getData() != null) {
ImportProgressDialogFragment.showDialog(getSupportFragmentManager(), getIntent().getData(), false);
documentUri = getIntent().getData();
isDirectory = false;
} else if (getIntent().getClipData() != null && getIntent().getClipData().getItemCount() > 0) {
ImportProgressDialogFragment.showDialog(getSupportFragmentManager(), getIntent().getClipData().getItemAt(0).getUri(), false);
documentUri = getIntent().getClipData().getItemAt(0).getUri();
isDirectory = false;
} else {
// Started from DirectoryChooserActivity
ImportProgressDialogFragment.showDialog(getSupportFragmentManager(), getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY), true);
documentUri = getIntent().getParcelableExtra(EXTRA_DIRECTORY_URI_KEY);
isDirectory = true;
}
documentFile = isDirectory ? DocumentFile.fromTreeUri(this, documentUri) : DocumentFile.fromSingleUri(this, documentUri);
toolbarTitle = getString(R.string.import_progress_message, documentFile.getName());
} else {
documentUri = savedInstanceState.getParcelable(BUNDLE_DOCUMENT_URI);
toolbarTitle = savedInstanceState.getString(BUNDLE_TOOLBAR_TITLE);
isDirectory = savedInstanceState.getBoolean(BUNDLE_IS_DIRECTORY);
documentFile = isDirectory ? DocumentFile.fromTreeUri(this, documentUri) : DocumentFile.fromSingleUri(this, documentUri);
}
toolbar.setTitle(toolbarTitle);
initViews();
viewModel = new ViewModelProvider(this).get(ImportViewModel.class);
viewModel.getImportData(documentFile).observe(this, data -> {
summary = data;
setProgress();
});
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(BUNDLE_ACTIVITY_RECREATED, true);
outState.putParcelable(BUNDLE_DOCUMENT_URI, documentUri);
outState.putBoolean(BUNDLE_IS_DIRECTORY, isDirectory);
outState.putString(BUNDLE_TOOLBAR_TITLE, toolbarTitle);
}
@Override
public void onImportCanceled(String directoryDisplayName, int successCount, int fileCount) {
onImportFinished(directoryDisplayName, successCount, fileCount);
public void onBackPressed() {
if (doubleBackToCancel || getTotalDone() == summary.getTotalCount()) {
super.onBackPressed();
viewModel.cancel();
getViewModelStore().clear();
return;
}
this.doubleBackToCancel = true;
Toast.makeText(this, getString(R.string.generic_click_twice_cancel), Toast.LENGTH_SHORT).show();
new Handler().postDelayed(() -> doubleBackToCancel=false, 2000);
}
@Override
public void onImportFinished(final String directoryDisplayName, final int successCount, final int fileCount) {
runOnUiThread(() -> ImportFinishedDialogFragment.showDialog(getSupportFragmentManager(), directoryDisplayName, successCount, fileCount));
private void initViews() {
viewDone.setText("0");
viewTotal.setText("0");
viewSummary.setText(getString(R.string.import_progress_review, 0, 0, 0, 0));
}
@Override
public void onDismissed() {
finish();
private int getTotalDone() {
return summary.getSuccessCount() + summary.getExistsCount() + summary.getErrorCount();
}
private void setProgress() {
int done = getTotalDone();
viewDone.setText("" + done);
viewTotal.setText("" + summary.getTotalCount());
viewProgressBar.setProgress((int) ((float) done / (float) summary.getTotalCount() * 100f));
viewSummary.setText(getString(R.string.import_progress_review, getTotalDone(), summary.getSuccessCount(), summary.getExistsCount(), summary.getErrorCount()));
if (done == summary.getTotalCount()) {
onImportEnded();
}
}
private void onImportEnded() {
viewAlertIcon.setVisibility(View.VISIBLE);
viewAlertMsg.setVisibility(View.VISIBLE);
viewRightButton.setVisibility(View.VISIBLE);
viewRightButton.setText(getString(R.string.generic_ok));
if (summary.getErrorCount() > 0) {
viewLeftButton.setVisibility(View.VISIBLE);
viewLeftButton.setText(getString(R.string.generic_show_errors));
viewLeftButton.setOnClickListener((view) -> ErrorListDialog.showDialog(getSupportFragmentManager(), getString(R.string.import_error_list_dialog_title), summary.getFileErrors()));
viewAlertIcon.setImageDrawable(getDrawable(R.drawable.ic_report_problem_24));
String msg = getResources().getQuantityString(R.plurals.generic_completed_with_errors, summary.getErrorCount(), summary.getErrorCount());
viewAlertMsg.setText(msg);
} else {
viewLeftButton.setVisibility(View.GONE);
viewAlertIcon.setImageDrawable(getDrawable(R.drawable.ic_dialog_success_24dp));
viewAlertMsg.setText(getString(R.string.generic_completed));
}
viewRightButton.setOnClickListener((view) -> {
getViewModelStore().clear();
finish();
});
}
}
@@ -0,0 +1,7 @@
package de.dennisguse.opentracks.io.file.importer;
class ImportAlreadyExistsException extends RuntimeException {
public ImportAlreadyExistsException(Exception e) {
super(e);
}
}
@@ -0,0 +1,11 @@
package de.dennisguse.opentracks.io.file.importer;
class ImportParserException extends RuntimeException {
public ImportParserException(String msg) {
super(msg);
}
public ImportParserException(Exception e) {
super(e);
}
}
@@ -0,0 +1,90 @@
package de.dennisguse.opentracks.io.file.importer;
import android.app.job.JobService;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.ResultReceiver;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.JobIntentService;
import androidx.documentfile.provider.DocumentFile;
import java.io.IOException;
import java.io.InputStream;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.content.data.Track;
import de.dennisguse.opentracks.io.file.TrackFileFormat;
import de.dennisguse.opentracks.util.FileUtils;
public class ImportService extends JobIntentService {
private static final String TAG = ImportService.class.getSimpleName();
private static final int JOB_ID = 2;
private static final String EXTRA_RECEIVER = "extra_receiver";
private static final String EXTRA_URI = "extra_uri";
private ResultReceiver resultReceiver;
public static void enqueue(Context context, ImportServiceResultReceiver receiver, Uri uri) {
Intent intent = new Intent(context, JobService.class);
intent.putExtra(EXTRA_RECEIVER, receiver);
intent.putExtra(EXTRA_URI, uri);
enqueueWork(context, ImportService.class, JOB_ID, intent);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
resultReceiver = intent.getParcelableExtra(EXTRA_RECEIVER);
Uri uri = intent.getParcelableExtra(EXTRA_URI);
importFile(DocumentFile.fromSingleUri(this, uri));
}
private void importFile(DocumentFile file) {
TrackImporter trackImporter;
String fileExtension = FileUtils.getExtension(file);
if (TrackFileFormat.GPX.getExtension().equals(fileExtension)) {
trackImporter = new GpxFileTrackImporter(this);
} else if (TrackFileFormat.KML_WITH_TRACKDETAIL_AND_SENSORDATA.getExtension().equals(fileExtension)) {
trackImporter = new KmlFileTrackImporter(this);
} else if (TrackFileFormat.KMZ_WITH_TRACKDETAIL_AND_SENSORDATA_AND_PICTURES.getExtension().equals(fileExtension)) {
trackImporter = new KmzTrackImporter(this, file.getUri());
} else {
Log.d(TAG, "Unsupported file format.");
sendResult(ImportServiceResultReceiver.RESULT_CODE_ERROR, null, file.getName(), getString(R.string.import_unsupported_format));
return;
}
try (InputStream inputStream = getContentResolver().openInputStream(file.getUri())) {
Track.Id trackId = trackImporter.importFile(inputStream);
if (trackId != null) {
sendResult(ImportServiceResultReceiver.RESULT_CODE_IMPORTED, trackId, file.getName(), getString(R.string.import_file_imported, file.getName()));
} else {
sendResult(ImportServiceResultReceiver.RESULT_CODE_ERROR, trackId, file.getName(), getString(R.string.import_unable_to_import_file, file.getName()));
}
} catch (IOException e) {
Log.d(TAG, "Unable to import file", e);
sendResult(ImportServiceResultReceiver.RESULT_CODE_ERROR, null, file.getName(), getString(R.string.import_unable_to_import_file, e.getMessage()));
} catch (ImportParserException e) {
Log.d(TAG, "Parser error: " + e.getMessage(), e);
sendResult(ImportServiceResultReceiver.RESULT_CODE_ERROR, null, file.getName(), getString(R.string.import_parser_error, e.getMessage()));
} catch (ImportAlreadyExistsException e) {
Log.d(TAG, "Track already exists: " + e.getMessage(), e);
sendResult(ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS, null, file.getName(), e.getMessage());
}
}
private void sendResult(int resultCode, Track.Id trackId, String fileName, String message) {
Bundle bundle = new Bundle();
bundle.putParcelable(ImportServiceResultReceiver.RESULT_EXTRA_TRACK_ID, trackId);
bundle.putString(ImportServiceResultReceiver.RESULT_EXTRA_FILENAME, fileName);
bundle.putString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE, message);
resultReceiver.send(resultCode, bundle);
}
}
@@ -0,0 +1,38 @@
package de.dennisguse.opentracks.io.file.importer;
import android.os.Bundle;
import android.os.Handler;
import android.os.ResultReceiver;
/**
* Create a new ResultReceive to receive results.
* Your {@link #onReceiveResult} method will be called from the thread running <var>handler</var> if given, or from an arbitrary thread if null.
*/
public class ImportServiceResultReceiver extends ResultReceiver {
public static final int RESULT_CODE_ERROR = 0;
public static final int RESULT_CODE_IMPORTED = 1;
public static final int RESULT_CODE_ALREADY_EXISTS = 2;
public static final String RESULT_EXTRA_TRACK_ID = "result_track_id";
public static final String RESULT_EXTRA_FILENAME = "result_extra_filename";
public static final String RESULT_EXTRA_MESSAGE = "result_extra_message";
private Receiver receiver;
public ImportServiceResultReceiver(Handler handler, Receiver receiver) {
super(handler);
this.receiver = receiver;
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (receiver != null) {
receiver.onReceiveResult(resultCode, resultData);
}
}
public interface Receiver {
void onReceiveResult(int resultCode, Bundle resultData);
}
}
@@ -0,0 +1,119 @@
package de.dennisguse.opentracks.io.file.importer;
import android.app.Application;
import android.os.Bundle;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.documentfile.provider.DocumentFile;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import java.util.ArrayList;
import java.util.List;
import de.dennisguse.opentracks.R;
import de.dennisguse.opentracks.util.FileUtils;
public class ImportViewModel extends AndroidViewModel implements ImportServiceResultReceiver.Receiver {
private static final String TAG = ImportViewModel.class.getSimpleName();
private MutableLiveData<Summary> importData;
private ImportServiceResultReceiver resultReceiver;
private Summary summary;
private boolean cancel = false;
private List<DocumentFile> filesToImport = new ArrayList<>();
public ImportViewModel(@NonNull Application application) {
super(application);
resultReceiver = new ImportServiceResultReceiver(new Handler(), this);
summary = new Summary();
}
public LiveData<Summary> getImportData(DocumentFile documentFile) {
if (importData == null) {
importData = new MutableLiveData<>();
loadData(documentFile);
}
return importData;
}
public void cancel() {
cancel = true;
}
private void loadData(DocumentFile documentFile) {
List<DocumentFile> fileList = FileUtils.getFiles(documentFile);
summary.totalCount = fileList.size();
for (DocumentFile df : fileList) {
filesToImport.add(df);
}
importNextFile();
}
private void importNextFile() {
if (cancel || filesToImport.isEmpty()) {
return;
}
ImportService.enqueue(getApplication(), resultReceiver, filesToImport.get(0).getUri());
filesToImport.remove(0);
}
@Override
public void onReceiveResult(int resultCode, Bundle resultData) {
if (resultData == null) {
throw new RuntimeException(TAG + ": onReceiveResult resultData NULL");
}
String fileName = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_FILENAME);
String message = resultData.getString(ImportServiceResultReceiver.RESULT_EXTRA_MESSAGE);
switch (resultCode) {
case ImportServiceResultReceiver.RESULT_CODE_ERROR:
summary.errorCount++;
summary.fileErrors.add(getApplication().getString(R.string.import_error_info, fileName, message));
break;
case ImportServiceResultReceiver.RESULT_CODE_IMPORTED:
summary.successCount++;
break;
case ImportServiceResultReceiver.RESULT_CODE_ALREADY_EXISTS:
summary.existsCount++;
break;
default:
throw new RuntimeException(TAG + ": import service result code invalid: " + resultCode);
}
importData.postValue(summary);
importNextFile();
}
public class Summary {
private int totalCount;
private int successCount;
private int existsCount;
private int errorCount;
private ArrayList<String> fileErrors = new ArrayList<>();
public int getTotalCount() {
return totalCount;
}
public int getSuccessCount() {
return successCount;
}
public int getExistsCount() {
return existsCount;
}
public int getErrorCount() {
return errorCount;
}
public ArrayList<String> getFileErrors() {
return fileErrors;
}
}
}
@@ -74,7 +74,13 @@ public class KmzTrackImporter implements TrackImporter {
return null;
}
trackId = findAndParseKmlFile(inputStream);
try {
trackId = findAndParseKmlFile(inputStream);
} catch (Exception e) {
cleanImport(context, importTrackId);
throw e;
}
if (trackId == null) {
cleanImport(context, importTrackId);
return null;
@@ -184,9 +190,15 @@ public class KmzTrackImporter implements TrackImporter {
zipInputStream.closeEntry();
}
return trackId;
} catch (ImportParserException e) {
Log.e(TAG, "Unable to import file", e);
throw e;
} catch (ImportAlreadyExistsException e) {
Log.e(TAG, "Unable to import file", e);
throw e;
} catch (IOException e) {
Log.e(TAG, "Unable to import file", e);
return null;
throw new ImportParserException(e);
}
}
@@ -242,11 +254,17 @@ public class KmzTrackImporter implements TrackImporter {
* @param zipInputStream the zip input stream
* @return the imported track id or -1L
*/
private Track.Id parseKml(ZipInputStream zipInputStream) throws IOException {
private Track.Id parseKml(ZipInputStream zipInputStream) {
KmlFileTrackImporter kmlFileTrackImporter = new KmlFileTrackImporter(context);
try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(getKml(zipInputStream))) {
return kmlFileTrackImporter.importFile(byteArrayInputStream);
} catch (ImportParserException e) {
throw e;
} catch (ImportAlreadyExistsException e) {
throw e;
} catch (Exception e) {
throw new ImportParserException(e);
}
}