diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f0d083c60..a93a40cf7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -68,6 +68,15 @@
+
+
+
diff --git a/app/src/main/java/protect/card_locker/ImportExportActivity.java b/app/src/main/java/protect/card_locker/ImportExportActivity.java
index 76399a2ee..ee230b49e 100644
--- a/app/src/main/java/protect/card_locker/ImportExportActivity.java
+++ b/app/src/main/java/protect/card_locker/ImportExportActivity.java
@@ -7,12 +7,15 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
+import android.provider.OpenableColumns;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
+import android.support.v4.content.FileProvider;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
@@ -24,6 +27,10 @@ import android.widget.Button;
import android.widget.Toast;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.util.List;
public class ImportExportActivity extends AppCompatActivity
@@ -132,24 +139,34 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(View v)
{
- startImport(exportFile);
+ 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(File target)
+ private void startImport(final InputStream target, final Uri targetUri)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@Override
- public void onTaskComplete(boolean success, File file)
+ public void onTaskComplete(boolean success)
{
- onImportComplete(success, file);
+ onImportComplete(success, targetUri);
}
};
importExporter = new ImportExportTask(ImportExportActivity.this,
- true, DataFormat.CSV, target, listener);
+ DataFormat.CSV, target, listener);
importExporter.execute();
}
@@ -158,14 +175,14 @@ public class ImportExportActivity extends AppCompatActivity
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@Override
- public void onTaskComplete(boolean success, File file)
+ public void onTaskComplete(boolean success)
{
- onExportComplete(success, file);
+ onExportComplete(success, exportFile);
}
};
importExporter = new ImportExportTask(ImportExportActivity.this,
- false, DataFormat.CSV, exportFile, listener);
+ DataFormat.CSV, exportFile, listener);
importExporter.execute();
}
@@ -220,7 +237,34 @@ public class ImportExportActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
- private void onImportComplete(boolean success, File path)
+ private String fileNameFromUri(Uri uri)
+ {
+ if("file".equals(uri.getScheme()))
+ {
+ return uri.getPath();
+ }
+
+ Cursor returnCursor =
+ getContentResolver().query(uri, null, null, null, null);
+ if(returnCursor == null)
+ {
+ return null;
+ }
+
+ int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
+ if(returnCursor.moveToFirst() == false)
+ {
+ returnCursor.close();
+ return null;
+ }
+
+ String name = returnCursor.getString(nameIndex);
+ returnCursor.close();
+
+ return name;
+ }
+
+ private void onImportComplete(boolean success, Uri path)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@@ -236,7 +280,15 @@ public class ImportExportActivity extends AppCompatActivity
int messageId = success ? R.string.importedFrom : R.string.importFailed;
final String template = getResources().getString(messageId);
- final String message = String.format(template, path.getAbsolutePath());
+
+ // Get the filename of the file being imported
+ String filename = fileNameFromUri(path);
+ if(filename == null)
+ {
+ filename = "(unknown)";
+ }
+
+ final String message = String.format(template, filename);
builder.setMessage(message);
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener()
{
@@ -286,11 +338,14 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(DialogInterface dialog, int which)
{
- Uri outputUri = Uri.fromFile(path);
+ Uri outputUri = FileProvider.getUriForFile(ImportExportActivity.this, BuildConfig.APPLICATION_ID, path);
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, outputUri);
sendIntent.setType("text/plain");
+ // set flag to give temporary permission to external app to use the FileProvider
+ sendIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
ImportExportActivity.this.startActivity(Intent.createChooser(sendIntent,
sendLabel));
@@ -344,34 +399,28 @@ public class ImportExportActivity extends AppCompatActivity
{
super.onActivityResult(requestCode, resultCode, data);
- if (resultCode == RESULT_OK && requestCode == CHOOSE_EXPORT_FILE)
- {
- String path = null;
-
- Uri uri = data.getData();
- if(uri != null && uri.toString().startsWith("/"))
- {
- uri = Uri.parse("file://" + uri.toString());
- }
-
- if(uri != null)
- {
- path = uri.getPath();
- }
-
- if(path != null)
- {
- Log.e(TAG, "Starting file import with: " + uri.toString());
- startImport(new File(path));
- }
- else
- {
- Log.e(TAG, "Fail to make sense of URI returned from activity: " + (uri != null ? uri.toString() : "null"));
- }
- }
- else
+ if (resultCode != RESULT_OK || requestCode != CHOOSE_EXPORT_FILE)
{
Log.w(TAG, "Failed onActivityResult(), result=" + resultCode);
+ return;
+ }
+
+ Uri uri = data.getData();
+ if(uri == null)
+ {
+ Log.e(TAG, "Activity returned a NULL URI");
+ return;
+ }
+
+ try
+ {
+ InputStream reader = getContentResolver().openInputStream(uri);
+ Log.e(TAG, "Starting file import with: " + uri.toString());
+ startImport(reader, uri);
+ }
+ catch (FileNotFoundException e)
+ {
+ Log.e(TAG, "Failed to import file: " + uri.toString(), e);
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/protect/card_locker/ImportExportTask.java b/app/src/main/java/protect/card_locker/ImportExportTask.java
index 5aceacb96..839e7c36d 100644
--- a/app/src/main/java/protect/card_locker/ImportExportTask.java
+++ b/app/src/main/java/protect/card_locker/ImportExportTask.java
@@ -12,6 +12,7 @@ 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.OutputStreamWriter;
import java.nio.charset.Charset;
@@ -24,29 +25,46 @@ class ImportExportTask extends AsyncTask
private boolean doImport;
private DataFormat format;
private File target;
+ private InputStream inputStream;
private TaskCompleteListener listener;
private ProgressDialog progress;
- public ImportExportTask(Activity activity, boolean doImport, DataFormat format, File target,
+ /**
+ * Constructor which will setup a task for exporting to the given file
+ */
+ ImportExportTask(Activity activity, DataFormat format, File target,
TaskCompleteListener listener)
{
super();
this.activity = activity;
- this.doImport = doImport;
+ this.doImport = false;
this.format = format;
this.target = target;
this.listener = listener;
}
- private boolean performImport(File importFile, DBHelper db)
+ /**
+ * Constructor which will setup a task for importing from the given InputStream.
+ */
+ ImportExportTask(Activity activity, DataFormat format, InputStream input,
+ TaskCompleteListener listener)
+ {
+ super();
+ this.activity = activity;
+ this.doImport = true;
+ this.format = format;
+ this.inputStream = input;
+ this.listener = listener;
+ }
+
+ private boolean performImport(InputStream stream, DBHelper db)
{
boolean result = false;
try
{
- FileInputStream fileReader = new FileInputStream(importFile);
- InputStreamReader reader = new InputStreamReader(fileReader, Charset.forName("UTF-8"));
+ InputStreamReader reader = new InputStreamReader(stream, Charset.forName("UTF-8"));
result = MultiFormatImporter.importData(db, reader, format);
reader.close();
}
@@ -55,7 +73,7 @@ class ImportExportTask extends AsyncTask
Log.e(TAG, "Unable to import file", e);
}
- Log.i(TAG, "Import of '" + importFile.getAbsolutePath() + "' result: " + result);
+ Log.i(TAG, "Import result: " + result);
return result;
}
@@ -105,7 +123,7 @@ class ImportExportTask extends AsyncTask
if(doImport)
{
- result = performImport(target, db);
+ result = performImport(inputStream, db);
}
else
{
@@ -117,7 +135,7 @@ class ImportExportTask extends AsyncTask
protected void onPostExecute(Boolean result)
{
- listener.onTaskComplete(result, target);
+ listener.onTaskComplete(result);
progress.dismiss();
Log.i(TAG, (doImport ? "Import" : "Export") + " Complete");
@@ -130,7 +148,7 @@ class ImportExportTask extends AsyncTask
}
interface TaskCompleteListener
{
- void onTaskComplete(boolean success, File file);
+ void onTaskComplete(boolean success);
}
}
diff --git a/app/src/main/res/xml/file_provider_paths.xml b/app/src/main/res/xml/file_provider_paths.xml
new file mode 100644
index 000000000..8bacf6557
--- /dev/null
+++ b/app/src/main/res/xml/file_provider_paths.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java
index 312b2439c..7f96e5519 100644
--- a/app/src/test/java/protect/card_locker/ImportExportTest.java
+++ b/app/src/test/java/protect/card_locker/ImportExportTest.java
@@ -17,6 +17,8 @@ import org.robolectric.annotation.Config;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
@@ -211,17 +213,15 @@ public class ImportExportTest
class TestTaskCompleteListener implements ImportExportTask.TaskCompleteListener
{
Boolean success;
- File file;
- public void onTaskComplete(boolean success, File file)
+ public void onTaskComplete(boolean success)
{
this.success = success;
- this.file = file;
}
}
@Test
- public void useImportExportTask()
+ public void useImportExportTask() throws FileNotFoundException
{
final int NUM_CARDS = 10;
@@ -235,7 +235,7 @@ public class ImportExportTest
TestTaskCompleteListener listener = new TestTaskCompleteListener();
// Export to the file
- ImportExportTask task = new ImportExportTask(activity, false, format, exportFile, listener);
+ ImportExportTask task = new ImportExportTask(activity, format, exportFile, listener);
task.execute();
// Actually run the task to completion
@@ -244,8 +244,6 @@ public class ImportExportTest
// Check that the listener was executed
assertNotNull(listener.success);
assertEquals(true, listener.success);
- assertNotNull(listener.file);
- assertEquals(exportFile, listener.file);
clearDatabase();
@@ -253,7 +251,9 @@ public class ImportExportTest
listener = new TestTaskCompleteListener();
- task = new ImportExportTask(activity, true, format, exportFile, listener);
+ FileInputStream fileStream = new FileInputStream(exportFile);
+
+ task = new ImportExportTask(activity, format, fileStream, listener);
task.execute();
// Actually run the task to completion
@@ -262,8 +262,6 @@ public class ImportExportTest
// Check that the listener was executed
assertNotNull(listener.success);
assertEquals(true, listener.success);
- assertNotNull(listener.file);
- assertEquals(exportFile, listener.file);
assertEquals(NUM_CARDS, db.getLoyaltyCardCount());