diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index ab9d4a2a1..7b6ca6d91 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -23,5 +23,11 @@ jobs: run: ./gradlew lintRelease - name: Run unit tests run: ./gradlew testReleaseUnitTest - - name: FindBugs - run: ./gradlew findbugs + - name: SpotBugs + run: ./gradlew spotbugsRelease + - name: Archive test results + if: always() + uses: actions/upload-artifact@v1 + with: + name: test-results + path: app/build/reports diff --git a/.gitignore b/.gitignore index 7473f08aa..7472a42d6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,9 @@ *.iml .gradle -/local.properties -/.idea/workspace.xml -/.idea/libraries +local.properties +.idea/ .DS_Store -/build -/captures -/app/release -/app/debug +build/ +captures/ +**/release +**/debug diff --git a/app/.gitignore b/app/.gitignore deleted file mode 100644 index 796b96d1c..000000000 --- a/app/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/app/build.gradle b/app/build.gradle index f055a2b3f..8281e9a4f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,13 +1,18 @@ -apply plugin: 'com.android.application' -apply plugin: 'findbugs' +import com.github.spotbugs.snom.SpotBugsTask -findbugs { - sourceSets = [] +apply plugin: 'com.android.application' +apply plugin: 'com.github.spotbugs' + +spotbugs { ignoreFailures = false + effort = 'max' + excludeFilter = file("./config/spotbugs/exclude.xml") + reportsDir = file("$buildDir/reports/spotbugs/") } android { compileSdkVersion 29 + buildToolsVersion "30.0.3" defaultConfig { applicationId "me.hackerchick.catima" @@ -15,63 +20,71 @@ android { targetSdkVersion 29 versionCode 53 versionName "1.6.1" - vectorDrawables.useSupportLibrary = true + + vectorDrawables.useSupportLibrary true } + buildTypes { release { - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + resValue "string", "app_name", "Catima" } debug { applicationIdSuffix ".debug" + resValue "string", "app_name", "Catima Debug" } } + + compileOptions { + encoding "UTF-8" + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + lintOptions { - disable "GoogleAppIndexingWarning" - disable "ButtonStyle" - disable "AlwaysShowAction" - disable "MissingTranslation" - disable "MissingPrefix" + disable "GoogleAppIndexingWarning", "ButtonStyle", "AlwaysShowAction", + "MissingTranslation", "MissingPrefix" } // Starting with Android Studio 3 Robolectric is unable to find resources. // The following allows it to find the resources. testOptions { unitTests { - includeAndroidResources = true + includeAndroidResources true } } } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) +// AndroidX implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'com.google.android.material:material:1.2.1' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'com.journeyapps:zxing-android-embedded:4.0.2@aar' - implementation 'com.google.zxing:core:3.3.0' - implementation 'org.apache.commons:commons-csv:1.5' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'com.jaredrummler:colorpicker:1.0.2' - implementation group: 'com.google.guava', name: 'guava', version: '20.0' - implementation "com.vanniktech:vntnumberpickerpreference:1.0.0" implementation 'androidx.cardview:cardview:1.0.0' - testImplementation 'junit:junit:4.12' - testImplementation "org.robolectric:robolectric:4.0.2" + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'androidx.preference:preference:1.1.1' + implementation 'com.google.android.material:material:1.2.1' + +// Third-party + implementation 'com.journeyapps:zxing-android-embedded:4.1.0@aar' + implementation 'com.google.zxing:core:3.4.1' + implementation 'org.apache.commons:commons-csv:1.8' + implementation 'com.jaredrummler:colorpicker:1.1.0' + implementation 'com.google.guava:guava:30.1-jre' + implementation 'com.github.invissvenska:NumberPickerPreference:1.0.1' + +// Testing + testImplementation 'junit:junit:4.13.1' + testImplementation 'org.robolectric:robolectric:4.0.2' } -task findbugs(type: FindBugs, dependsOn: 'assembleDebug') { +tasks.withType(SpotBugsTask) { - description 'Run findbugs' + description 'Run spotbugs' group 'verification' - classes = fileTree('build/intermediates/javac/debug/compileDebugJavaWithJavac/classes') - source = fileTree('src/main/java') - classpath = files() - - effort = 'max' - - excludeFilter = file("./config/findbugs/exclude.xml") + //classes = fileTree('build/intermediates/javac/debug/compileDebugJavaWithJavac/classes') + //source = fileTree('src/main/java') + //classpath = files() reports { xml.enabled = false diff --git a/app/config/findbugs/exclude.xml b/app/config/spotbugs/exclude.xml similarity index 100% rename from app/config/findbugs/exclude.xml rename to app/config/spotbugs/exclude.xml diff --git a/app/src/androidTest/java/protect/card_locker/ApplicationTest.java b/app/src/androidTest/java/protect/card_locker/ApplicationTest.java deleted file mode 100644 index 2e3339b0e..000000000 --- a/app/src/androidTest/java/protect/card_locker/ApplicationTest.java +++ /dev/null @@ -1,15 +0,0 @@ -package protect.card_locker; - -import android.app.Application; -import android.test.ApplicationTestCase; - -/** - * Testing Fundamentals - */ -public class ApplicationTest extends ApplicationTestCase -{ - public ApplicationTest() - { - super(Application.class); - } -} diff --git a/app/src/main/java/protect/card_locker/AboutActivity.java b/app/src/main/java/protect/card_locker/AboutActivity.java index 1c3efad38..0367ad75b 100644 --- a/app/src/main/java/protect/card_locker/AboutActivity.java +++ b/app/src/main/java/protect/card_locker/AboutActivity.java @@ -41,7 +41,7 @@ public class AboutActivity extends AppCompatActivity .put("ZXing", "https://github.com/zxing/zxing") .put("ZXing Android Embedded", "https://github.com/journeyapps/zxing-android-embedded") .put("Color Picker", "https://github.com/jaredrummler/ColorPicker") - .put("VNTNumberPickerPreference", "https://github.com/vanniktech/VNTNumberPickerPreference") + .put("NumberPickerPreference", "https://github.com/invissvenska/NumberPickerPreference") .build(); final Map USED_ASSETS = ImmutableMap.of diff --git a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java index 400436ec5..6f68d318a 100644 --- a/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java +++ b/app/src/main/java/protect/card_locker/CsvDatabaseImporter.java @@ -47,6 +47,8 @@ public class CsvDatabaseImporter implements DatabaseImporter default: throw new FormatException(String.format("No code to parse version %s", version)); } + + bufferedReader.close(); } public void parseV1(DBHelper db, BufferedReader input) throws IOException, FormatException, InterruptedException @@ -131,7 +133,6 @@ public class CsvDatabaseImporter implements DatabaseImporter } } - public void parseV2Groups(DBHelper db, SQLiteDatabase database, String data) throws IOException, FormatException, InterruptedException { // Parse groups @@ -145,10 +146,10 @@ public class CsvDatabaseImporter implements DatabaseImporter throw new InterruptedException(); } } - - groupParser.close(); } catch (IllegalArgumentException | IllegalStateException e) { throw new FormatException("Issue parsing CSV data", e); + } finally { + groupParser.close(); } } @@ -165,10 +166,10 @@ public class CsvDatabaseImporter implements DatabaseImporter throw new InterruptedException(); } } - - cardParser.close(); } catch (IllegalArgumentException | IllegalStateException e) { throw new FormatException("Issue parsing CSV data", e); + } finally { + cardParser.close(); } } @@ -185,10 +186,10 @@ public class CsvDatabaseImporter implements DatabaseImporter throw new InterruptedException(); } } - - cardGroupParser.close(); } catch (IllegalArgumentException | IllegalStateException e) { throw new FormatException("Issue parsing CSV data", e); + } finally { + cardGroupParser.close(); } } diff --git a/app/src/main/java/protect/card_locker/DBHelper.java b/app/src/main/java/protect/card_locker/DBHelper.java index ce5ea4035..5536eaaa6 100644 --- a/app/src/main/java/protect/card_locker/DBHelper.java +++ b/app/src/main/java/protect/card_locker/DBHelper.java @@ -215,6 +215,7 @@ public class DBHelper extends SQLiteOpenHelper List groups = new ArrayList<>(); if (!data.moveToFirst()) { + data.close(); return groups; } @@ -224,6 +225,8 @@ public class DBHelper extends SQLiteOpenHelper groups.add(Group.toGroup(data)); } + data.close(); + return groups; } @@ -395,6 +398,7 @@ public class DBHelper extends SQLiteOpenHelper List groups = new ArrayList<>(); if (!data.moveToFirst()) { + data.close(); return groups; } @@ -404,6 +408,8 @@ public class DBHelper extends SQLiteOpenHelper groups.add(Group.toGroup(data)); } + data.close(); + return groups; } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java index 8b46ffbc7..48ad0950f 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardEditActivity.java @@ -446,7 +446,7 @@ public class LoyaltyCardEditActivity extends AppCompatActivity // Nothing to do, no change made } }); - dialog.show(getFragmentManager(), "color-picker-dialog"); + dialog.show(getSupportFragmentManager(), "color-picker-dialog"); } } diff --git a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java index 77b2b8d01..557395972 100644 --- a/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java +++ b/app/src/main/java/protect/card_locker/LoyaltyCardViewActivity.java @@ -73,6 +73,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity private Drawable getIcon(int icon, boolean dark) { Drawable unwrappedIcon = AppCompatResources.getDrawable(this, icon); + assert unwrappedIcon != null; Drawable wrappedIcon = DrawableCompat.wrap(unwrappedIcon); if(dark) { @@ -123,7 +124,7 @@ public class LoyaltyCardViewActivity extends AppCompatActivity barcodeImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - if(barcodeIsFullscreen) + if (barcodeIsFullscreen) { setFullscreen(false); } diff --git a/app/src/main/java/protect/card_locker/MainActivity.java b/app/src/main/java/protect/card_locker/MainActivity.java index ef091fccd..1faf8f23f 100644 --- a/app/src/main/java/protect/card_locker/MainActivity.java +++ b/app/src/main/java/protect/card_locker/MainActivity.java @@ -120,13 +120,13 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O Object group = null; if (groupsTabLayout.getTabCount() != 0) { - TabLayout.Tab tab = groupsTabLayout.getTabAt(0); - - if (selectedTab < groupsTabLayout.getTabCount()) { - tab = groupsTabLayout.getTabAt(selectedTab); + TabLayout.Tab tab = groupsTabLayout.getTabAt(selectedTab); + if (tab == null) { + tab = groupsTabLayout.getTabAt(0); } groupsTabLayout.selectTab(tab); + assert tab != null; group = tab.getTag(); } updateLoyaltyCardList(filter, group); @@ -361,8 +361,13 @@ public class MainActivity extends AppCompatActivity implements GestureDetector.O filter = newText; TabLayout groupsTabLayout = findViewById(R.id.groups); + TabLayout.Tab currentTab = groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition()); + + updateLoyaltyCardList( + newText, + currentTab != null ? currentTab.getTag() : null + ); - updateLoyaltyCardList(newText, groupsTabLayout.getTabCount() > 0 ? groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition()).getTag() : null); return true; } }); diff --git a/app/src/main/java/protect/card_locker/MultiFormatImporter.java b/app/src/main/java/protect/card_locker/MultiFormatImporter.java index a4ab4c83c..6a27af7e4 100644 --- a/app/src/main/java/protect/card_locker/MultiFormatImporter.java +++ b/app/src/main/java/protect/card_locker/MultiFormatImporter.java @@ -31,32 +31,23 @@ public class MultiFormatImporter break; } - if(importer != null) + if (importer != null) { try { importer.importData(db, input); return true; } - catch(IOException e) + catch(IOException | FormatException | InterruptedException e) { - Log.e(TAG, "Failed to input data", e); - } - catch(FormatException e) - { - Log.e(TAG, "Failed to input data", e); - } - catch(InterruptedException e) - { - Log.e(TAG, "Failed to input data", e); + Log.e(TAG, "Failed to import data", e); } - return false; } else { Log.e(TAG, "Unsupported data format imported: " + format.name()); - return false; } + return false; } } diff --git a/app/src/main/java/protect/card_locker/preferences/Settings.java b/app/src/main/java/protect/card_locker/preferences/Settings.java index 8be423b17..b6808f352 100644 --- a/app/src/main/java/protect/card_locker/preferences/Settings.java +++ b/app/src/main/java/protect/card_locker/preferences/Settings.java @@ -2,7 +2,7 @@ package protect.card_locker.preferences; import android.content.Context; import android.content.SharedPreferences; -import android.preference.PreferenceManager; +import androidx.preference.PreferenceManager; import androidx.annotation.IntegerRes; import androidx.annotation.StringRes; import androidx.appcompat.app.AppCompatDelegate; diff --git a/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java b/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java index 485626742..8674e8a33 100644 --- a/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java +++ b/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java @@ -1,13 +1,17 @@ package protect.card_locker.preferences; import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; +import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentActivity; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; import android.view.MenuItem; +import nl.invissvenska.numberpickerpreference.NumberDialogPreference; +import nl.invissvenska.numberpickerpreference.NumberPickerPreferenceDialogFragment; import protect.card_locker.R; public class SettingsActivity extends AppCompatActivity @@ -20,11 +24,11 @@ public class SettingsActivity extends AppCompatActivity ActionBar actionBar = getSupportActionBar(); if(actionBar != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); } // Display the fragment as the main content. - getFragmentManager().beginTransaction() + getSupportFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } @@ -43,17 +47,18 @@ public class SettingsActivity extends AppCompatActivity return super.onOptionsItemSelected(item); } - public static class SettingsFragment extends PreferenceFragment + public static class SettingsFragment extends PreferenceFragmentCompat { + private static final String DIALOG_FRAGMENT_TAG = "SettingsFragment"; @Override - public void onCreate(final Bundle savedInstanceState) + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { - super.onCreate(savedInstanceState); - // Load the preferences from an XML resource addPreferencesFromResource(R.xml.preferences); - findPreference(getResources().getString(R.string.settings_key_theme)).setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() + Preference themePreference = findPreference(getResources().getString(R.string.settings_key_theme)); + assert themePreference != null; + themePreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override public boolean onPreferenceChange(Preference preference, Object o) @@ -71,11 +76,36 @@ public class SettingsActivity extends AppCompatActivity AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); } - getActivity().recreate(); + FragmentActivity activity = getActivity(); + if (activity != null) { + activity.recreate(); + } return true; } }); } + + @Override + public void onDisplayPreferenceDialog(Preference preference) + { + if (preference instanceof NumberDialogPreference) + { + NumberDialogPreference dialogPreference = (NumberDialogPreference) preference; + DialogFragment dialogFragment = NumberPickerPreferenceDialogFragment + .newInstance( + dialogPreference.getKey(), + dialogPreference.getMinValue(), + dialogPreference.getMaxValue(), + dialogPreference.getUnitText() + ); + dialogFragment.setTargetFragment(this, 0); + dialogFragment.show(getParentFragmentManager(), DIALOG_FRAGMENT_TAG); + } + else + { + super.onDisplayPreferenceDialog(preference); + } + } } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 18cd8e6b2..d75bd4cf7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,7 +2,6 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2" xmlns:tools="http://schemas.android.com/tools"> - Catima Search Add diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 54a2a9397..b176a17a7 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -4,52 +4,60 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> + android:title="@string/settings_category_title_ui" + app:iconSpaceReserved="false"> + android:entryValues="@array/theme_values" + app:iconSpaceReserved="false" /> - + app:defaultValue="@integer/settings_card_title_list_font_size_sp" + app:numberPickerPreference_maxValue="@integer/settings_card_title_list_max_font_size_sp" + app:numberPickerPreference_minValue="@integer/settings_card_title_list_min_font_size_sp" + app:iconSpaceReserved="false" /> - + app:defaultValue="@integer/settings_card_note_list_font_size_sp" + app:numberPickerPreference_maxValue="@integer/settings_card_note_list_max_font_size_sp" + app:numberPickerPreference_minValue="@integer/settings_card_note_list_min_font_size_sp" + app:iconSpaceReserved="false" /> - + app:defaultValue="@integer/settings_card_title_font_size_sp" + app:numberPickerPreference_maxValue="@integer/settings_card_title_max_font_size_sp" + app:numberPickerPreference_minValue="@integer/settings_card_title_min_font_size_sp" + app:iconSpaceReserved="false" /> - + app:defaultValue="@integer/settings_card_id_font_size_sp" + app:numberPickerPreference_maxValue="@integer/settings_card_id_max_font_size_sp" + app:numberPickerPreference_minValue="@integer/settings_card_id_min_font_size_sp" + app:iconSpaceReserved="false" /> - + android:title="@string/settings_display_barcode_max_brightness" + app:iconSpaceReserved="false" /> - + android:title="@string/settings_lock_barcode_orientation" + app:iconSpaceReserved="false" /> \ No newline at end of file diff --git a/app/src/test/java/protect/card_locker/DatabaseTest.java b/app/src/test/java/protect/card_locker/DatabaseTest.java index d9fc87520..2d4f06f0f 100644 --- a/app/src/test/java/protect/card_locker/DatabaseTest.java +++ b/app/src/test/java/protect/card_locker/DatabaseTest.java @@ -170,6 +170,8 @@ public class DatabaseTest } assertTrue(cursor.isAfterLast()); + + cursor.close(); } @Test @@ -227,6 +229,8 @@ public class DatabaseTest } assertTrue(cursor.isAfterLast()); + + cursor.close(); } private void setupDatabaseVersion1(SQLiteDatabase database) diff --git a/app/src/test/java/protect/card_locker/ImportExportTest.java b/app/src/test/java/protect/card_locker/ImportExportTest.java index 64c13bd80..55dd508d7 100644 --- a/app/src/test/java/protect/card_locker/ImportExportTest.java +++ b/app/src/test/java/protect/card_locker/ImportExportTest.java @@ -224,6 +224,8 @@ public class ImportExportTest { SQLiteDatabase database = db.getWritableDatabase(); database.execSQL("delete from " + DBHelper.LoyaltyCardDbIds.TABLE); + database.execSQL("delete from " + DBHelper.LoyaltyCardDbGroups.TABLE); + database.execSQL("delete from " + DBHelper.LoyaltyCardDbIdsGroups.TABLE); database.close(); assertEquals(0, db.getLoyaltyCardCount()); @@ -454,6 +456,8 @@ public class ImportExportTest assertEquals(false, result); assertEquals(0, db.getLoyaltyCardCount()); + + clearDatabase(); } } @@ -549,6 +553,8 @@ public class ImportExportTest assertEquals("type", card.barcodeType); assertEquals(0, card.starStatus); assertNull(card.headerColor); + + clearDatabase(); } @Test @@ -582,6 +588,8 @@ public class ImportExportTest assertEquals("type", card.barcodeType); assertEquals(0, card.starStatus); assertNull(card.headerColor); + + clearDatabase(); } @Test @@ -606,6 +614,8 @@ public class ImportExportTest boolean result = MultiFormatImporter.importData(db, inStream, DataFormat.CSV); assertEquals(false, result); assertEquals(0, db.getLoyaltyCardCount()); + + clearDatabase(); } @Test @@ -639,6 +649,8 @@ public class ImportExportTest assertEquals("", card.barcodeType); assertEquals(0, card.starStatus); assertEquals(1, (long) card.headerColor); + + clearDatabase(); } @Test @@ -672,10 +684,10 @@ public class ImportExportTest assertEquals("type", card.barcodeType); assertEquals(1, card.starStatus); assertEquals(1, (long) card.headerColor); + + clearDatabase(); } - - @Test public void importWithNoStarredFieldV1() throws IOException { @@ -707,6 +719,8 @@ public class ImportExportTest assertEquals("type", card.barcodeType); assertEquals(0, card.starStatus); assertEquals(1, (long) card.headerColor); + + clearDatabase(); } @Test @@ -760,5 +774,7 @@ public class ImportExportTest assertEquals("type", card.barcodeType); assertEquals(0, card.starStatus); assertEquals(1, (long) card.headerColor); + + clearDatabase(); } } diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java index 2aee8e2a0..c0f6de264 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java @@ -4,7 +4,7 @@ import android.app.Activity; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Color; -import android.preference.PreferenceManager; +import androidx.preference.PreferenceManager; import android.view.View; import android.widget.ImageView; import android.widget.TextView; @@ -93,6 +93,8 @@ public class LoyaltyCardCursorAdapterTest View view = createView(cursor); checkView(view, card.store, card.note, false); + + cursor.close(); } @Test @@ -107,6 +109,8 @@ public class LoyaltyCardCursorAdapterTest View view = createView(cursor); checkView(view, card.store, card.note, false); + + cursor.close(); } @Test @@ -125,6 +129,8 @@ public class LoyaltyCardCursorAdapterTest setFontSizes(30, 31); view = createView(cursor); checkView(view, card.store, card.note, true); + + cursor.close(); } @Test @@ -134,7 +140,6 @@ public class LoyaltyCardCursorAdapterTest db.insertLoyaltyCard("storeB", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); db.insertLoyaltyCard("storeC", "note", "cardId", BarcodeFormat.UPC_A.toString(), Color.BLACK, 1); - Cursor cursor = db.getLoyaltyCardCursor(); cursor.moveToFirst(); View view = createView(cursor); @@ -150,5 +155,7 @@ public class LoyaltyCardCursorAdapterTest view = createView(cursor); star = view.findViewById(R.id.star); assertEquals(View.GONE, star.getVisibility()); + + cursor.close(); } } diff --git a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java index 232ffde41..92c9dd413 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java @@ -17,7 +17,7 @@ import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.net.Uri; import android.os.Bundle; -import android.preference.PreferenceManager; +import androidx.preference.PreferenceManager; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -139,6 +139,8 @@ public class LoyaltyCardViewActivityTest } assertNotNull(card.headerColor); assertNotNull(card.headerTextColor); + + db.close(); } /** @@ -287,6 +289,8 @@ public class LoyaltyCardViewActivityTest noteField.setText("note"); activity.findViewById(R.id.fabSave).performClick(); assertEquals(0, db.getLoyaltyCardCount()); + + db.close(); } @Test @@ -416,6 +420,8 @@ public class LoyaltyCardViewActivityTest activityController.resume(); checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", BARCODE_DATA, BARCODE_TYPE); + + db.close(); } @Test @@ -432,6 +438,8 @@ public class LoyaltyCardViewActivityTest activityController.resume(); checkAllFields(activity, ViewMode.VIEW_CARD, "store", "note", BARCODE_DATA, BARCODE_TYPE); + + db.close(); } @Test @@ -453,6 +461,8 @@ public class LoyaltyCardViewActivityTest captureBarcodeWithResult(activity, true); checkAllFields(activity, ViewMode.UPDATE_CARD, "store", "note", BARCODE_DATA, BARCODE_TYPE); + + db.close(); } @Test @@ -488,6 +498,8 @@ public class LoyaltyCardViewActivityTest shadowOf(activity).clickMenuItem(android.R.id.home); assertEquals(false, activity.hasChanged); assertEquals(true, activity.isFinishing()); + + db.close(); } @Test @@ -512,6 +524,8 @@ public class LoyaltyCardViewActivityTest assertEquals("Block Rotation", menu.findItem(R.id.action_lock_unlock).getTitle().toString()); assertEquals("Share", menu.findItem(R.id.action_share).getTitle().toString()); assertEquals("Add to favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString()); + + db.close(); } @Test @@ -550,6 +564,8 @@ public class LoyaltyCardViewActivityTest assertEquals(false, activity.isFinishing()); shadowOf(activity).clickMenuItem(android.R.id.home); assertEquals(true, activity.isFinishing()); + + db.close(); } @Test @@ -568,6 +584,8 @@ public class LoyaltyCardViewActivityTest assertEquals(false, activity.isFinishing()); shadowOf(activity).clickMenuItem(android.R.id.home); assertEquals(true, activity.isFinishing()); + + db.close(); } @Test @@ -585,6 +603,8 @@ public class LoyaltyCardViewActivityTest // Save and check the loyalty card saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, BARCODE_TYPE, false); + + db.close(); } @Test @@ -602,6 +622,8 @@ public class LoyaltyCardViewActivityTest // Save and check the loyalty card saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false); + + db.close(); } @Test @@ -629,6 +651,8 @@ public class LoyaltyCardViewActivityTest // Check if the special NO_BARCODE string doesn't get saved saveLoyaltyCardWithArguments(activity, "store", "note", BARCODE_DATA, activity.getApplicationContext().getString(R.string.noBarcode), false); + + db.close(); } @Test @@ -665,6 +689,8 @@ public class LoyaltyCardViewActivityTest shadowOf(activity).clickMenuItem(android.R.id.home); assertEquals(true, activity.isFinishing()); + + db.close(); } @Test @@ -701,6 +727,8 @@ public class LoyaltyCardViewActivityTest String title = item.getTitle().toString(); assertEquals(title, activity.getString(R.string.lockScreen)); } + + db.close(); } } @@ -731,6 +759,8 @@ public class LoyaltyCardViewActivityTest shadowOf(activity).clickMenuItem(R.id.action_star_unstar); assertEquals("Add to favorites", menu.findItem(R.id.action_star_unstar).getTitle().toString()); + + db.close(); } @Test @@ -794,6 +824,8 @@ public class LoyaltyCardViewActivityTest // Pressing back when not in full screen should finish activity activity.onBackPressed(); assertEquals(true, activity.isFinishing()); + + db.close(); } @Test diff --git a/app/src/test/java/protect/card_locker/MainActivityTest.java b/app/src/test/java/protect/card_locker/MainActivityTest.java index ba57759d5..05cb3b229 100644 --- a/app/src/test/java/protect/card_locker/MainActivityTest.java +++ b/app/src/test/java/protect/card_locker/MainActivityTest.java @@ -111,6 +111,8 @@ public class MainActivityTest assertEquals(1, list.getAdapter().getCount()); Cursor cursor = (Cursor)list.getAdapter().getItem(0); assertNotNull(cursor); + + db.close(); } @Test @@ -161,6 +163,8 @@ public class MainActivityTest cursor = (Cursor)list.getAdapter().getItem(3); assertNotNull(cursor); assertEquals("storeB",cursor.getString(cursor.getColumnIndex("store"))); + + db.close(); } @Test @@ -209,6 +213,8 @@ public class MainActivityTest activityController.pause(); activityController.resume(); assertEquals(0, groupTabs.getTabCount()); + + db.close(); } @Test @@ -434,5 +440,7 @@ public class MainActivityTest assertEquals(View.VISIBLE, list.getVisibility()); assertEquals(2, list.getCount()); + + db.close(); } } \ No newline at end of file diff --git a/build.gradle b/build.gradle index e95b2f8d5..90a00d30e 100644 --- a/build.gradle +++ b/build.gradle @@ -2,13 +2,15 @@ buildscript { repositories { - jcenter() - - // For the Android Gradle plugin google() + jcenter() + maven { url "https://jitpack.io" } + maven { url "https://plugins.gradle.org/m2/" } } + dependencies { - classpath 'com.android.tools.build:gradle:3.2.0' + classpath 'com.android.tools.build:gradle:4.1.1' + classpath 'gradle.plugin.com.github.spotbugs.snom:spotbugs-gradle-plugin:4.6.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -17,9 +19,10 @@ buildscript { allprojects { repositories { - jcenter() - maven { url 'https://jitpack.io' } google() + jcenter() + maven { url "https://jitpack.io" } + maven { url "https://plugins.gradle.org/m2/" } } } diff --git a/gradle.properties b/gradle.properties index a74d1fa9b..acf164f6c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,11 +16,5 @@ # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true - -# The aapt2 tool creates an APK which fails to install on Android 5 and below if it contains -# a bug. Build tools 27.0.1 has a mitigation. Avoiding aapt2 also avoids hitting the bug. -# See: https://issuetracker.google.com/issues/64434571 -android.enableAapt2=false android.enableJetifier=true android.useAndroidX=true -android.enableUnitTestBinaryResources=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 05ef575b0..e708b1c02 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9013ee43f..1f3fdbc52 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ -#Sun Jul 19 15:12:56 CEST 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-all.zip diff --git a/gradlew b/gradlew index 9d82f7891..4f906e0c8 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,20 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ############################################################################## ## @@ -6,42 +22,6 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; -esac - # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" @@ -60,8 +40,49 @@ cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -85,7 +106,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -105,10 +126,11 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath @@ -134,27 +156,30 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=`save "$@"` -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec99730b..ac1b06f93 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -8,20 +24,23 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if "%ERRORLEVEL%" == "0" goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -35,7 +54,7 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% @@ -45,34 +64,14 @@ echo location of your Java installation. goto fail -:init -@rem Get command-line arguments, handling Windowz variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* :end @rem End local scope for the variables with windows NT shell