Support importing content URIs and file URIs

When importing backed up settings other activities may provide
data via a content URI. This is especially likely on Android 7+,
where providing a file URI is flagged as a security issue.
To support such activities, this commit enables supporting
content URIs for importing settings.
This commit is contained in:
Branden Archer
2017-11-21 18:15:56 -05:00
parent 2ebc862e27
commit 91e3f9f785
3 changed files with 64 additions and 31 deletions

View File

@@ -24,6 +24,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 +136,34 @@ public class ImportExportActivity extends AppCompatActivity
@Override
public void onClick(View v)
{
startImport(exportFile);
String fileUri = exportFile.toURI().toString();
try
{
FileInputStream stream = new FileInputStream(exportFile);
startImport(stream, fileUri);
}
catch(FileNotFoundException e)
{
Log.e(TAG, "Could not import file " + exportFile.getAbsolutePath(), e);
onImportComplete(false, fileUri);
}
}
});
}
private void startImport(File target)
private void startImport(final InputStream target, final String targetUri)
{
ImportExportTask.TaskCompleteListener listener = new ImportExportTask.TaskCompleteListener()
{
@Override
public void onTaskComplete(boolean success)
{
onImportComplete(success, exportFile);
onImportComplete(success, targetUri);
}
};
importExporter = new ImportExportTask(ImportExportActivity.this,
true, DataFormat.CSV, target, listener);
DataFormat.CSV, target, listener);
importExporter.execute();
}
@@ -165,7 +179,7 @@ public class ImportExportActivity extends AppCompatActivity
};
importExporter = new ImportExportTask(ImportExportActivity.this,
false, DataFormat.CSV, exportFile, listener);
DataFormat.CSV, exportFile, listener);
importExporter.execute();
}
@@ -220,7 +234,7 @@ public class ImportExportActivity extends AppCompatActivity
return super.onOptionsItemSelected(item);
}
private void onImportComplete(boolean success, File path)
private void onImportComplete(boolean success, String path)
{
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@@ -236,7 +250,7 @@ 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());
final String message = String.format(template, path);
builder.setMessage(message);
builder.setNeutralButton(R.string.ok, new DialogInterface.OnClickListener()
{
@@ -346,27 +360,24 @@ public class ImportExportActivity extends AppCompatActivity
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));
try
{
InputStream reader = getContentResolver().openInputStream(uri);
Log.e(TAG, "Starting file import with: " + uri.toString());
startImport(reader, uri.toString());
}
catch (FileNotFoundException e)
{
Log.e(TAG, "Failed to import file: " + uri.toString(), e);
}
}
else
{
Log.e(TAG, "Fail to make sense of URI returned from activity: " + (uri != null ? uri.toString() : "null"));
Log.e(TAG, "Activity returned a NULL URI");
}
}
else

View File

@@ -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<Void, Void, Boolean>
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<Void, Void, Boolean>
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<Void, Void, Boolean>
if(doImport)
{
result = performImport(target, db);
result = performImport(inputStream, db);
}
else
{

View File

@@ -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;
@@ -219,7 +221,7 @@ public class ImportExportTest
}
@Test
public void useImportExportTask()
public void useImportExportTask() throws FileNotFoundException
{
final int NUM_CARDS = 10;
@@ -233,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
@@ -249,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