diff --git a/app/src/main/java/protect/card_locker/MainActivity.java b/app/src/main/java/protect/card_locker/MainActivity.java index 4a21297a6..a8e97c298 100644 --- a/app/src/main/java/protect/card_locker/MainActivity.java +++ b/app/src/main/java/protect/card_locker/MainActivity.java @@ -54,6 +54,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard public static final String RESTART_ACTIVITY_INTENT = "restart_activity_intent"; private static final int MEDIUM_SCALE_FACTOR_DIP = 460; + static final String STATE_SEARCH_QUERY = "SEARCH_QUERY"; private SQLiteDatabase mDatabase; private LoyaltyCardCursorAdapter mAdapter; @@ -61,6 +62,8 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard private SearchView mSearchView; private int mLoyaltyCardCount = 0; protected String mFilter = ""; + private String currentQuery = ""; + private String finalQuery = ""; protected Object mGroup = null; protected DBHelper.LoyaltyCardOrder mOrder = DBHelper.LoyaltyCardOrder.Alpha; protected DBHelper.LoyaltyCardOrderDirection mOrderDirection = DBHelper.LoyaltyCardOrderDirection.Ascending; @@ -70,9 +73,7 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard private View mNoMatchingCardsText; private View mNoGroupCardsText; private TabLayout groupsTabLayout; - private Runnable mUpdateLoyaltyCardListRunnable; - private ActivityResultLauncher mBarcodeScannerLauncher; private ActivityResultLauncher mSettingsLauncher; @@ -199,7 +200,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard protected void onCreate(Bundle inputSavedInstanceState) { SplashScreen.installSplashScreen(this); super.onCreate(inputSavedInstanceState); - // We should extract the share intent after we called the super.onCreate as it may need to spawn a dialog window and the app needs to be initialized to not crash extractIntentFields(getIntent()); @@ -298,7 +298,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard if (mSearchView != null && !mSearchView.isIconified()) { mFilter = mSearchView.getQuery().toString(); } - // Start of active tab logic updateTabGroups(groupsTabLayout); @@ -513,6 +512,24 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard } + @Override + // Saving currentQuery to finalQuery for user, this will be used to restore search history, happens when user clicks a card from list + protected void onSaveInstanceState(@NonNull Bundle outState) { + super.onSaveInstanceState(outState); + finalQuery = currentQuery; + // Putting the query also into outState for later use in onRestoreInstanceState when rotating screen + if (mSearchView != null) { + outState.putString(STATE_SEARCH_QUERY, finalQuery); + } + } + + @Override + // Restoring instance state when rotation of screen happens with the goal to restore search query for user + protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + finalQuery = savedInstanceState.getString(STATE_SEARCH_QUERY, ""); + } + @Override public boolean onCreateOptionsMenu(Menu inputMenu) { getMenuInflater().inflate(R.menu.main_menu, inputMenu); @@ -525,7 +542,6 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard mSearchView = (SearchView) searchMenuItem.getActionView(); mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); mSearchView.setSubmitButtonEnabled(false); - mSearchView.setOnCloseListener(() -> { invalidateOptionsMenu(); return false; @@ -550,6 +566,9 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard mSearchView.clearFocus(); return false; } + currentQuery = ""; + mFilter = ""; + updateLoyaltyCardList(false); return true; } }); @@ -564,7 +583,21 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard @Override public boolean onQueryTextChange(String newText) { mFilter = newText; - + // New logic to ensure search history after coming back from picked card - user will see the last search query + if (newText.isEmpty()) { + if(!finalQuery.isEmpty()){ + // Setting the query text for user after coming back from picked card from finalQuery + mSearchView.setQuery(finalQuery, false); + } + else if(!currentQuery.isEmpty()){ + // Else if is needed in case user deletes search - expected behaviour is to show all cards + currentQuery = ""; + mSearchView.setQuery(currentQuery, false); + } + } else { + // Setting search query each time user changes the text in search to temporary variable to be used later in finalQuery String which will be used to restore search history + currentQuery = newText; + } TabLayout.Tab currentTab = groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition()); mGroup = currentTab != null ? currentTab.getTag() : null; @@ -573,6 +606,14 @@ public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCard return true; } }); + // Check if we came from a picked card back to search, in that case we want to show the search view with previous search query + if(!finalQuery.isEmpty()){ + // Expand the search view to show the query + searchMenuItem.expandActionView(); + // Setting the query text to empty String due to behaviour of onQueryTextChange after coming back from picked card - onQueryTextChange is called automatically without users interaction + finalQuery = ""; + mSearchView.setQuery(currentQuery, false); + } } return super.onCreateOptionsMenu(inputMenu); diff --git a/app/src/test/java/protect/card_locker/MainActivityTest.java b/app/src/test/java/protect/card_locker/MainActivityTest.java index 3ee7f8c14..1fb4a5aa2 100644 --- a/app/src/test/java/protect/card_locker/MainActivityTest.java +++ b/app/src/test/java/protect/card_locker/MainActivityTest.java @@ -3,18 +3,22 @@ package protect.card_locker; import static android.os.Looper.getMainLooper; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.robolectric.Shadows.shadowOf; import android.app.Activity; import android.content.ComponentName; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; import android.graphics.Color; import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.appcompat.widget.SearchView; import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.tabs.TabLayout; @@ -210,9 +214,12 @@ public class MainActivityTest { @Test public void testFiltering() { + // FIXME: This test directly sets mFilter instead of using mSearchView, making it not test the search flow correctly + // It may falsely succeed or fail, but we're leaving it here so we at least test something instead of nothing ActivityController activityController = Robolectric.buildActivity(MainActivity.class).create(); MainActivity mainActivity = (MainActivity) activityController.get(); + activityController.start(); activityController.resume(); @@ -244,6 +251,16 @@ public class MainActivityTest { activityController.pause(); activityController.resume(); + Configuration configuration = mainActivity.getResources().getConfiguration(); + configuration.orientation = Configuration.ORIENTATION_LANDSCAPE; + mainActivity.onConfigurationChanged(configuration); + + configuration.orientation = Configuration.ORIENTATION_PORTRAIT; + mainActivity.onConfigurationChanged(configuration); + + configuration.orientation = Configuration.ORIENTATION_LANDSCAPE; + mainActivity.onConfigurationChanged(configuration); + assertEquals(View.GONE, helpSection.getVisibility()); assertEquals(View.GONE, noMatchingCardsText.getVisibility()); assertEquals(View.VISIBLE, list.getVisibility()); @@ -337,6 +354,12 @@ public class MainActivityTest { mainActivity.mFilter = "second"; + configuration.orientation = Configuration.ORIENTATION_LANDSCAPE; + mainActivity.onConfigurationChanged(configuration); + + configuration.orientation = Configuration.ORIENTATION_PORTRAIT; + mainActivity.onConfigurationChanged(configuration); + activityController.pause(); activityController.resume(); @@ -371,6 +394,10 @@ public class MainActivityTest { mainActivity.mFilter = "company"; + // Rotate to landscape (right) + configuration.orientation = Configuration.ORIENTATION_LANDSCAPE; + mainActivity.onConfigurationChanged(configuration); + activityController.pause(); activityController.resume(); @@ -443,4 +470,43 @@ public class MainActivityTest { database.close(); } + + @Test + public void testSearchQueryRestorationAfterNavigatingBack() { + ActivityController activityController = Robolectric.buildActivity(MainActivity.class).create(); + + MainActivity mainActivity = (MainActivity) activityController.get(); + activityController.start(); + activityController.resume(); + + final Menu menu = shadowOf(Robolectric.setupActivity(MainActivity.class)).getOptionsMenu(); + MenuItem searchMenuItem = menu.findItem(R.id.action_search); + SearchView mSearchView = (SearchView) searchMenuItem.getActionView(); + + + SQLiteDatabase database = TestHelpers.getEmptyDb(mainActivity).getWritableDatabase(); + DBHelper.insertLoyaltyCard(database, "The First Store", "Initial note", null, null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), Color.BLACK, 0, null,0); + DBHelper.insertLoyaltyCard(database, "The Second Store", "Secondary note", null, null, new BigDecimal("0"), null, "cardId", null, CatimaBarcode.fromBarcode(BarcodeFormat.UPC_A), Color.BLACK, 0, null,0); + + String finalQuery = "store"; + assert mSearchView != null; + mSearchView.setQuery(finalQuery, false); + + activityController.pause(); + activityController.resume(); + + // Simulation of what happens when users comes back after picking up card + // We simulate expanding and setting the Query that we want to restore (in code it is from finalQuery String) + searchMenuItem.expandActionView(); + + mSearchView.setQuery(finalQuery, false); + + activityController.pause(); + activityController.resume(); + + assertTrue(searchMenuItem.isActionViewExpanded()); + assertEquals("store", mSearchView.getQuery().toString()); + + database.close(); + } } \ No newline at end of file