diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index bf4599aaf..7b6ca6d91 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -24,7 +24,7 @@ jobs: - name: Run unit tests run: ./gradlew testReleaseUnitTest - name: SpotBugs - run: ./gradlew spotbugs + run: ./gradlew spotbugsRelease - name: Archive test results if: always() uses: actions/upload-artifact@v1 diff --git a/app/config/spotbugs/exclude.xml b/app/config/spotbugs/exclude.xml index ebedfe3af..8f7997b7c 100644 --- a/app/config/spotbugs/exclude.xml +++ b/app/config/spotbugs/exclude.xml @@ -1,4 +1,4 @@ - + @@ -7,4 +7,4 @@ - + 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/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/SettingsActivity.java b/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java index 5ba0b9b2a..8674e8a33 100644 --- a/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java +++ b/app/src/main/java/protect/card_locker/preferences/SettingsActivity.java @@ -2,6 +2,7 @@ package protect.card_locker.preferences; import android.os.Bundle; import androidx.fragment.app.DialogFragment; +import androidx.fragment.app.FragmentActivity; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.appcompat.app.ActionBar; @@ -23,7 +24,7 @@ public class SettingsActivity extends AppCompatActivity ActionBar actionBar = getSupportActionBar(); if(actionBar != null) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + actionBar.setDisplayHomeAsUpEnabled(true); } // Display the fragment as the main content. @@ -55,7 +56,9 @@ public class SettingsActivity extends AppCompatActivity // 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) @@ -73,7 +76,10 @@ public class SettingsActivity extends AppCompatActivity AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); } - getActivity().recreate(); + FragmentActivity activity = getActivity(); + if (activity != null) { + activity.recreate(); + } return true; } }); 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 c21f0f623..c0f6de264 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardCursorAdapterTest.java @@ -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 bb79f75a2..92c9dd413 100644 --- a/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java +++ b/app/src/test/java/protect/card_locker/LoyaltyCardViewActivityTest.java @@ -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