Add ability to select file to import

To date the only way to import a file is to hope the exported file
is in the correct location. To remove this requirement, this change
checks if there are any activities available on the system which
can look in the file system. If there are, new options for
importing data will be available.

In addition, the import/export activity's layout was updated,
adding more explanation.

Translation contributions:
Clonewayx: cs
PanderMusubi: nl
pbeckmann: de
Airon90: it
arno-github : fr
This commit is contained in:
Branden Archer
2017-01-16 22:05:14 -05:00
parent 89f649e5ef
commit eeb41376c5
13 changed files with 722 additions and 121 deletions

View File

@@ -0,0 +1,154 @@
package protect.card_locker;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.view.View;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.res.builder.RobolectricPackageManager;
import static org.robolectric.Shadows.shadowOf;
import static org.junit.Assert.assertEquals;
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 17)
public class ImportExportActivityTest
{
private void registerIntentHandler(String handler)
{
// Add something that will 'handle' the given intent type
RobolectricPackageManager packageManager = (RobolectricPackageManager) shadowOf(
RuntimeEnvironment.application).getPackageManager();
ResolveInfo info = new ResolveInfo();
info.isDefault = true;
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.packageName = "does.not.matter";
info.activityInfo = new ActivityInfo();
info.activityInfo.applicationInfo = applicationInfo;
info.activityInfo.name = "DoesNotMatter";
info.activityInfo.exported = true;
Intent intent = new Intent(handler);
if(handler.equals(Intent.ACTION_PICK))
{
intent.setData(Uri.parse("file://"));
}
if(handler.equals(Intent.ACTION_GET_CONTENT))
{
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
}
packageManager.addResolveInfoForIntent(intent, info);
}
private void checkVisibility(Activity activity, int state, int divider, int title, int message, int button)
{
View dividerView = activity.findViewById(divider);
View titleView = activity.findViewById(title);
View messageView = activity.findViewById(message);
View buttonView = activity.findViewById(button);
assertEquals(state, dividerView.getVisibility());
assertEquals(state, titleView.getVisibility());
assertEquals(state, messageView.getVisibility());
assertEquals(state, buttonView.getVisibility());
}
@Test
public void testImportFilesystemOption()
{
for(boolean isInstalled : new Boolean[]{false, true})
{
int visibility = isInstalled ? View.VISIBLE : View.GONE;
if(isInstalled)
{
registerIntentHandler(Intent.ACTION_PICK);
}
Activity activity = Robolectric.setupActivity(ImportExportActivity.class);
checkVisibility(activity, visibility, R.id.dividerImportFilesystem,
R.id.importOptionFilesystemTitle, R.id.importOptionFilesystemExplanation,
R.id.importOptionFilesystemButton);
// Should always be gone, as its provider is never installed
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);
}
}
@Test
public void testImportApplicationOption()
{
for(boolean isInstalled : new Boolean[]{false, true})
{
int visibility = isInstalled ? View.VISIBLE : View.GONE;
if(isInstalled)
{
registerIntentHandler(Intent.ACTION_GET_CONTENT);
}
Activity activity = Robolectric.setupActivity(ImportExportActivity.class);
checkVisibility(activity, visibility, R.id.dividerImportApplication,
R.id.importOptionApplicationTitle, R.id.importOptionApplicationExplanation,
R.id.importOptionApplicationButton);
// Should always be gone, as its provider is never installed
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);
}
}
@Test
public void testAllOptionsAvailable()
{
registerIntentHandler(Intent.ACTION_PICK);
registerIntentHandler(Intent.ACTION_GET_CONTENT);
Activity activity = Robolectric.setupActivity(ImportExportActivity.class);
checkVisibility(activity, View.VISIBLE, R.id.dividerImportApplication,
R.id.importOptionApplicationTitle, R.id.importOptionApplicationExplanation,
R.id.importOptionApplicationButton);
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.os.Environment;
import com.google.zxing.BarcodeFormat;
@@ -15,12 +16,14 @@ import org.robolectric.annotation.Config;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Calendar;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@RunWith(RobolectricGradleTestRunner.class)
@@ -205,32 +208,63 @@ public class ImportExportTest
}
}
class TestTaskCompleteListener implements ImportExportTask.TaskCompleteListener
{
Boolean success;
File file;
public void onTaskComplete(boolean success, File file)
{
this.success = success;
this.file = file;
}
}
@Test
public void useImportExportTask()
{
final int NUM_CARDS = 10;
final File sdcardDir = Environment.getExternalStorageDirectory();
final File exportFile = new File(sdcardDir, "LoyaltyCardLocker.csv");
for(DataFormat format : DataFormat.values())
{
addLoyaltyCards(NUM_CARDS);
// Export to whatever the default location is
ImportExportTask task = new ImportExportTask(activity, false, format);
TestTaskCompleteListener listener = new TestTaskCompleteListener();
// Export to the file
ImportExportTask task = new ImportExportTask(activity, false, format, exportFile, listener);
task.execute();
// Actually run the task to completion
Robolectric.flushBackgroundThreadScheduler();
// Check that the listener was executed
assertNotNull(listener.success);
assertEquals(true, listener.success);
assertNotNull(listener.file);
assertEquals(exportFile, listener.file);
clearDatabase();
// Import everything back from the default location
task = new ImportExportTask(activity, true, format);
listener = new TestTaskCompleteListener();
task = new ImportExportTask(activity, true, format, exportFile, listener);
task.execute();
// Actually run the task to completion
Robolectric.flushBackgroundThreadScheduler();
// 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());
checkLoyaltyCards();