From ce4dd33eab241ed79233a44706fb0ce00ba24dd1 Mon Sep 17 00:00:00 2001
From: litetex <40789489+litetex@users.noreply.github.com>
Date: Fri, 31 Dec 2021 14:50:27 +0100
Subject: [PATCH] Fixed problems with Android's lifecycle (restore)

---
 .../newpipe/settings/SettingsActivity.java    | 84 +++++++++++++++----
 .../PreferenceSearchFragment.java             |  8 +-
 2 files changed, 73 insertions(+), 19 deletions(-)

diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
index 25d22dc9f..4dfbf6c08 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java
@@ -3,6 +3,7 @@ package org.schabi.newpipe.settings;
 import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
 
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -40,6 +41,9 @@ import org.schabi.newpipe.views.FocusOverlayView;
 
 import java.util.concurrent.TimeUnit;
 
+import icepick.Icepick;
+import icepick.State;
+
 /*
  * Created by Christian Schabesberger on 31.08.15.
  *
@@ -77,20 +81,37 @@ public class SettingsActivity extends AppCompatActivity implements
     private View searchContainer;
     private EditText searchEditText;
 
+    // State
+    @State
+    String searchText;
+    @State
+    boolean wasSearchActive;
+
     @Override
     protected void onCreate(final Bundle savedInstanceBundle) {
         setTheme(ThemeHelper.getSettingsThemeStyle(this));
         assureCorrectAppLanguage(this);
+
         super.onCreate(savedInstanceBundle);
+        Icepick.restoreInstanceState(this, savedInstanceBundle);
+        final boolean restored = savedInstanceBundle != null;
 
         final SettingsLayoutBinding settingsLayoutBinding =
                 SettingsLayoutBinding.inflate(getLayoutInflater());
         setContentView(settingsLayoutBinding.getRoot());
-        initSearch(settingsLayoutBinding);
+        initSearch(settingsLayoutBinding, restored);
 
         setSupportActionBar(settingsLayoutBinding.settingsToolbarLayout.toolbar);
 
-        if (savedInstanceBundle == null) {
+        if (restored) {
+            // Restore state
+            if (this.wasSearchActive) {
+                setSearchActive(true);
+                if (!TextUtils.isEmpty(this.searchText)) {
+                    this.searchEditText.setText(this.searchText);
+                }
+            }
+        } else {
             getSupportFragmentManager().beginTransaction()
                     .replace(R.id.settings_fragment_holder, new MainSettingsFragment())
                     .commit();
@@ -101,6 +122,12 @@ public class SettingsActivity extends AppCompatActivity implements
         }
     }
 
+    @Override
+    protected void onSaveInstanceState(@NonNull final Bundle outState) {
+        super.onSaveInstanceState(outState);
+        Icepick.saveInstanceState(this, outState);
+    }
+
     @Override
     public boolean onCreateOptionsMenu(final Menu menu) {
         final ActionBar actionBar = getSupportActionBar();
@@ -175,7 +202,10 @@ public class SettingsActivity extends AppCompatActivity implements
     //////////////////////////////////////////////////////////////////////////*/
     //region Search
 
-    private void initSearch(final SettingsLayoutBinding settingsLayoutBinding) {
+    private void initSearch(
+            final SettingsLayoutBinding settingsLayoutBinding,
+            final boolean restored
+    ) {
         searchContainer =
                 settingsLayoutBinding.settingsToolbarLayout.toolbar
                         .findViewById(R.id.toolbar_search_container);
@@ -207,7 +237,19 @@ public class SettingsActivity extends AppCompatActivity implements
                 .map(parser::parse)
                 .forEach(searcher::add);
 
-        searchFragment = new PreferenceSearchFragment(searcher);
+        if (restored) {
+            searchFragment = (PreferenceSearchFragment) getSupportFragmentManager()
+                    .findFragmentByTag(PreferenceSearchFragment.NAME);
+            if (searchFragment != null) {
+                // Hide/Remove the search fragment otherwise we get an exception
+                // when adding it (because it's already present)
+                hideSearchFragment();
+            }
+        }
+        if (searchFragment == null) {
+            searchFragment = new PreferenceSearchFragment();
+        }
+        searchFragment.setSearcher(searcher);
     }
 
     private void prepareSearchConfig() {
@@ -228,36 +270,45 @@ public class SettingsActivity extends AppCompatActivity implements
 
     public void setMenuSearchItem(final MenuItem menuSearchItem) {
         this.menuSearchItem = menuSearchItem;
+
+        // Ensure that the item is in the correct state when adding it. This is due to
+        // Android's lifecycle (the Activity is recreated before the Fragment that registers this)
+        if (menuSearchItem != null) {
+            menuSearchItem.setVisible(!isSearchActive());
+        }
     }
 
     public void setSearchActive(final boolean active) {
+        if (DEBUG) {
+            Log.d(TAG, "setSearchActive called active=" + active);
+        }
+
         // Ignore if search is already in correct state
         if (isSearchActive() == active) {
             return;
         }
 
-        if (DEBUG) {
-            Log.d(TAG, "setSearchActive called active=" + active);
-        }
+        wasSearchActive = active;
 
         searchContainer.setVisibility(active ? View.VISIBLE : View.GONE);
         if (menuSearchItem != null) {
             menuSearchItem.setVisible(!active);
         }
 
-        final FragmentManager fm = getSupportFragmentManager();
         if (active) {
-            fm.beginTransaction()
+            getSupportFragmentManager()
+                    .beginTransaction()
                     .add(FRAGMENT_HOLDER_ID, searchFragment, PreferenceSearchFragment.NAME)
                     .addToBackStack(PreferenceSearchFragment.NAME)
                     .commit();
 
             KeyboardUtil.showKeyboard(this, searchEditText);
         } else if (searchFragment != null) {
-            fm.beginTransaction().remove(searchFragment).commit();
-            fm.popBackStack(
-                    PreferenceSearchFragment.NAME,
-                    FragmentManager.POP_BACK_STACK_INCLUSIVE);
+            hideSearchFragment();
+            getSupportFragmentManager()
+                    .popBackStack(
+                        PreferenceSearchFragment.NAME,
+                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
 
             KeyboardUtil.hideKeyboard(this, searchEditText);
         }
@@ -265,6 +316,10 @@ public class SettingsActivity extends AppCompatActivity implements
         resetSearchText();
     }
 
+    private void hideSearchFragment() {
+        getSupportFragmentManager().beginTransaction().remove(searchFragment).commit();
+    }
+
     private void resetSearchText() {
         searchEditText.setText("");
     }
@@ -279,7 +334,8 @@ public class SettingsActivity extends AppCompatActivity implements
         }
 
         if (searchFragment != null) {
-            searchFragment.updateSearchResults(this.searchEditText.getText().toString());
+            searchText = this.searchEditText.getText().toString();
+            searchFragment.updateSearchResults(searchText);
         }
     }
 
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
index 1b1d627c1..38c87ea76 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
@@ -24,14 +24,12 @@ import java.util.Objects;
 public class PreferenceSearchFragment extends Fragment {
     public static final String NAME = PreferenceSearchFragment.class.getSimpleName();
 
-    private final PreferenceSearcher searcher;
+    private PreferenceSearcher searcher;
 
     private SearchViewHolder viewHolder;
     private PreferenceSearchAdapter adapter;
 
-    public PreferenceSearchFragment(
-            final PreferenceSearcher searcher
-    ) {
+    public void setSearcher(final PreferenceSearcher searcher) {
         this.searcher = searcher;
     }
 
@@ -56,7 +54,7 @@ public class PreferenceSearchFragment extends Fragment {
     }
 
     public void updateSearchResults(final String keyword) {
-        if (adapter == null) {
+        if (adapter == null || searcher == null) {
             return;
         }