Choose export location

This commit is contained in:
Sylvia van Os
2020-01-28 21:00:07 +01:00
parent 7970bb8a40
commit c94c3cb47e
6 changed files with 74 additions and 117 deletions

View File

@@ -10,15 +10,14 @@ import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
@@ -28,7 +27,9 @@ import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
public class ImportExportActivity extends AppCompatActivity
@@ -36,13 +37,11 @@ public class ImportExportActivity extends AppCompatActivity
private static final String TAG = "LoyaltyCardLocker";
private static final int PERMISSIONS_EXTERNAL_STORAGE = 1;
private static final int CHOOSE_EXPORT_FILE = 2;
private static final int CHOOSE_EXPORT_LOCATION = 2;
private static final int CHOOSE_EXPORTED_FILE = 3;
private ImportExportTask importExporter;
private final File sdcardDir = Environment.getExternalStorageDirectory();
private final File exportFile = new File(sdcardDir, "LoyaltyCardKeychain.csv");
@Override
protected void onCreate(Bundle savedInstanceState)
{
@@ -70,6 +69,11 @@ public class ImportExportActivity extends AppCompatActivity
PERMISSIONS_EXTERNAL_STORAGE);
}
// Check that there is a file manager available
final Intent intentCreateDocumentAction = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intentCreateDocumentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentCreateDocumentAction.setType("text/csv");
intentCreateDocumentAction.putExtra(Intent.EXTRA_TITLE, "LoyaltyCardKeychain.csv");
Button exportButton = findViewById(R.id.exportButton);
exportButton.setOnClickListener(new View.OnClickListener()
@@ -77,7 +81,7 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(View v)
{
startExport();
chooseFileWithIntent(intentCreateDocumentAction, CHOOSE_EXPORT_LOCATION);
}
});
@@ -86,14 +90,13 @@ public class ImportExportActivity extends AppCompatActivity
intentGetContentAction.addCategory(Intent.CATEGORY_OPENABLE);
intentGetContentAction.setType("*/*");
Button importFilesystem = findViewById(R.id.importOptionFilesystemButton);
importFilesystem.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentGetContentAction);
chooseFileWithIntent(intentGetContentAction, CHOOSE_EXPORTED_FILE);
}
});
@@ -114,7 +117,7 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(View v)
{
chooseFileWithIntent(intentPickAction);
chooseFileWithIntent(intentPickAction, CHOOSE_EXPORTED_FILE);
}
});
@@ -125,28 +128,6 @@ public class ImportExportActivity extends AppCompatActivity
findViewById(R.id.importOptionApplicationExplanation).setVisibility(View.GONE);
importApplication.setVisibility(View.GONE);
}
// This option, to import from the fixed location, should always be present
Button importButton = findViewById(R.id.importOptionFixedButton);
importButton.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(View v)
{
Uri uri = Uri.fromFile(exportFile);
try
{
FileInputStream stream = new FileInputStream(exportFile);
startImport(stream, uri);
}
catch(FileNotFoundException e)
{
Log.e(TAG, "Could not import file " + exportFile.getAbsolutePath(), e);
onImportComplete(false, uri);
}
}
});
}
private void startImport(final InputStream target, final Uri targetUri)
@@ -165,19 +146,19 @@ public class ImportExportActivity extends AppCompatActivity
importExporter.execute();
}
private void startExport()
private void startExport(final OutputStream target, final Uri targetUri)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@Override
public void onTaskComplete(boolean success)
{
onExportComplete(success, exportFile);
onExportComplete(success, targetUri);
}
};
importExporter = new ImportExportTask(ImportExportActivity.this,
DataFormat.CSV, exportFile, listener);
DataFormat.CSV, target, listener);
importExporter.execute();
}
@@ -265,7 +246,7 @@ public class ImportExportActivity extends AppCompatActivity
builder.create().show();
}
private void onExportComplete(boolean success, final File path)
private void onExportComplete(boolean success, final Uri path)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@@ -281,7 +262,11 @@ public class ImportExportActivity extends AppCompatActivity
int messageId = success ? R.string.exportedTo : R.string.exportFailed;
final String template = getResources().getString(messageId);
final String message = String.format(template, path.getAbsolutePath());
// Get the filename of the file being imported
String filename = path.toString();
final String message = String.format(template, filename);
builder.setMessage(message);
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener()
{
@@ -301,9 +286,8 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(DialogInterface dialog, int which)
{
Uri outputUri = FileProvider.getUriForFile(ImportExportActivity.this, BuildConfig.APPLICATION_ID, path);
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, outputUri);
sendIntent.putExtra(Intent.EXTRA_STREAM, path);
sendIntent.setType("text/csv");
// set flag to give temporary permission to external app to use the FileProvider
@@ -345,11 +329,11 @@ public class ImportExportActivity extends AppCompatActivity
return false;
}
private void chooseFileWithIntent(Intent intent)
private void chooseFileWithIntent(Intent intent, int requestCode)
{
try
{
startActivityForResult(intent, CHOOSE_EXPORT_FILE);
startActivityForResult(intent, requestCode);
}
catch (ActivityNotFoundException e)
{
@@ -362,7 +346,7 @@ public class ImportExportActivity extends AppCompatActivity
{
super.onActivityResult(requestCode, resultCode, data);
if (resultCode != RESULT_OK || requestCode != CHOOSE_EXPORT_FILE)
if (resultCode != RESULT_OK || (requestCode != CHOOSE_EXPORT_LOCATION && requestCode != CHOOSE_EXPORTED_FILE))
{
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
return;
@@ -377,24 +361,48 @@ public class ImportExportActivity extends AppCompatActivity
try
{
InputStream reader;
if(uri.getScheme() != null)
if (requestCode == CHOOSE_EXPORT_LOCATION)
{
reader = getContentResolver().openInputStream(uri);
OutputStream writer;
if (uri.getScheme() != null)
{
writer = getContentResolver().openOutputStream(uri);
}
else
{
writer = new FileOutputStream(new File(uri.toString()));
}
Log.e(TAG, "Starting file export with: " + uri.toString());
startExport(writer, uri);
}
else
{
reader = new FileInputStream(new File(uri.toString()));
}
InputStream reader;
if(uri.getScheme() != null)
{
reader = getContentResolver().openInputStream(uri);
}
else
{
reader = new FileInputStream(new File(uri.toString()));
}
Log.e(TAG, "Starting file import with: " + uri.toString());
startImport(reader, uri);
Log.e(TAG, "Starting file export with: " + uri.toString());
startImport(reader, uri);
}
}
catch(FileNotFoundException e)
{
Log.e(TAG, "Failed to import file: " + uri.toString(), e);
onImportComplete(false, uri);
Log.e(TAG, "Failed to import/export file: " + uri.toString(), e);
if (requestCode == CHOOSE_EXPORT_LOCATION)
{
onExportComplete(false, uri);
}
else
{
onImportComplete(false, uri);
}
}
}
}

View File

@@ -4,16 +4,12 @@ import android.app.Activity;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
@@ -24,7 +20,7 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
private Activity activity;
private boolean doImport;
private DataFormat format;
private File target;
private OutputStream outputStream;
private InputStream inputStream;
private TaskCompleteListener listener;
@@ -33,14 +29,14 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
/**
* Constructor which will setup a task for exporting to the given file
*/
ImportExportTask(Activity activity, DataFormat format, File target,
ImportExportTask(Activity activity, DataFormat format, OutputStream output,
TaskCompleteListener listener)
{
super();
this.activity = activity;
this.doImport = false;
this.format = format;
this.target = target;
this.outputStream = output;
this.listener = listener;
}
@@ -78,14 +74,13 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
return result;
}
private boolean performExport(File exportFile, DBHelper db)
private boolean performExport(OutputStream stream, DBHelper db)
{
boolean result = false;
try
{
FileOutputStream fileWriter = new FileOutputStream(exportFile);
OutputStreamWriter writer = new OutputStreamWriter(fileWriter, Charset.forName("UTF-8"));
OutputStreamWriter writer = new OutputStreamWriter(stream, Charset.forName("UTF-8"));
result = MultiFormatExporter.exportData(db, writer, format);
writer.close();
}
@@ -94,7 +89,7 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
Log.e(TAG, "Unable to export file", e);
}
Log.i(TAG, "Export of '" + exportFile.getAbsolutePath() + "' result: " + result);
Log.i(TAG, "Export result: " + result);
return result;
}
@@ -127,7 +122,7 @@ class ImportExportTask extends AsyncTask<Void, Void, Boolean>
}
else
{
result = performExport(target, db);
result = performExport(outputStream, db);
}
return result;

View File

@@ -99,7 +99,6 @@
android:layout_marginTop="8dp"
android:text="@string/importOptionFilesystemButton" />
<View
android:id="@+id/dividerImportApplication"
android:layout_width="fill_parent"
@@ -129,36 +128,6 @@
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:text="@string/importOptionApplicationButton" />
<View
android:id="@+id/dividerImportFixed"
android:layout_width="fill_parent"
android:layout_height="1dp"
android:layout_margin="16dp"
android:background="?android:attr/listDivider"/>
<TextView
android:id="@+id/importOptionFixedTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_large"
android:text="@string/importOptionFixedTitle"/>
<TextView
android:id="@+id/importOptionFixedExplanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:textSize="@dimen/text_size_medium"
android:text="@string/importOptionFixedExplanation"/>
<Button
android:id="@+id/importOptionFixedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="8dp"
android:text="@string/importOptionFixedButton" />
</LinearLayout>
</ScrollView>

View File

@@ -65,16 +65,13 @@
<string name="importing">Importing&#8230;</string>
<string name="exporting">Exporting&#8230;</string>
<string name="noExternalStoragePermissionError">Unable to import or export cards without the external storage permission</string>
<string name="exportOptionExplanation">Data is written to the top directory in external storage.</string>
<string name="exportOptionExplanation">Data will be written to a location of your choice.</string>
<string name="importOptionFilesystemTitle">Import from filesystem</string>
<string name="importOptionFilesystemExplanation">Choose a specific file from the filesystem.</string>
<string name="importOptionFilesystemButton">From filesystem</string>
<string name="importOptionApplicationTitle">Use external application</string>
<string name="importOptionApplicationExplanation">Use an external application like Dropbox, Google Drive, or your favorite file manager to open a file.</string>
<string name="importOptionApplicationButton">Use external application</string>
<string name="importOptionFixedTitle">Import from export location</string>
<string name="importOptionFixedExplanation">Import from the same location on the filesystem that is written to on export.</string>
<string name="importOptionFixedButton">Use export location</string>
<string name="about">About</string>
<string name="app_copyright_fmt">Copyright 2016-<xliff:g>%d</xliff:g> Branden Archer</string>

View File

@@ -84,12 +84,6 @@ public class ImportExportActivityTest
checkVisibility(activity, View.GONE, R.id.dividerImportApplication,
R.id.importOptionApplicationTitle, R.id.importOptionApplicationExplanation,
R.id.importOptionApplicationButton);
// Import from file system should always be present
checkVisibility(activity, View.VISIBLE, R.id.dividerImportFixed,
R.id.importOptionFixedTitle, R.id.importOptionFixedExplanation,
R.id.importOptionFixedButton);
}
}
@@ -115,12 +109,6 @@ public class ImportExportActivityTest
checkVisibility(activity, View.GONE, R.id.dividerImportFilesystem,
R.id.importOptionFilesystemTitle, R.id.importOptionFilesystemExplanation,
R.id.importOptionFilesystemButton);
// Import from file system should always be present
checkVisibility(activity, View.VISIBLE, R.id.dividerImportFixed,
R.id.importOptionFixedTitle, R.id.importOptionFixedExplanation,
R.id.importOptionFixedButton);
}
}
@@ -139,9 +127,5 @@ public class ImportExportActivityTest
checkVisibility(activity, View.VISIBLE, R.id.dividerImportFilesystem,
R.id.importOptionFilesystemTitle, R.id.importOptionFilesystemExplanation,
R.id.importOptionFilesystemButton);
checkVisibility(activity, View.VISIBLE, R.id.dividerImportFixed,
R.id.importOptionFixedTitle, R.id.importOptionFixedExplanation,
R.id.importOptionFixedButton);
}
}

View File

@@ -3,6 +3,7 @@ package protect.card_locker;
import android.app.Activity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Environment;
import com.google.zxing.BarcodeFormat;
@@ -20,9 +21,11 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Calendar;
@@ -245,7 +248,8 @@ public class ImportExportTest
TestTaskCompleteListener listener = new TestTaskCompleteListener();
// Export to the file
ImportExportTask task = new ImportExportTask(activity, format, exportFile, listener);
FileOutputStream fileOutputStream = new FileOutputStream(exportFile);
ImportExportTask task = new ImportExportTask(activity, format, fileOutputStream, listener);
task.execute();
// Actually run the task to completion