From f77e2a6c560e9c3fbba07f13279def716e1ac44f Mon Sep 17 00:00:00 2001 From: Brayan Oliveira <69634269+brayandso@users.noreply.github.com> Date: Fri, 6 Dec 2024 15:56:54 -0300 Subject: [PATCH] refactor: remove PreferencesActivity and upgrade the SearchPreference library, which was what made the removal possible --- AnkiDroid/src/main/AndroidManifest.xml | 9 ---- .../main/java/com/ichi2/anki/DeckPicker.kt | 4 +- .../ichi2/anki/NavigationDrawerActivity.kt | 4 +- .../ichi2/anki/preferences/HeaderFragment.kt | 41 +++++++++++-------- .../com/ichi2/anki/preferences/Preferences.kt | 23 +---------- .../anki/ActivityStartupUnderBackupTest.kt | 2 - .../ichi2/anki/preferences/PreferencesTest.kt | 19 ++++----- .../anki/preferences/SettingsSearchBarTest.kt | 11 ++--- .../java/com/ichi2/testutils/ActivityList.kt | 2 - gradle/libs.versions.toml | 4 +- 10 files changed, 45 insertions(+), 74 deletions(-) diff --git a/AnkiDroid/src/main/AndroidManifest.xml b/AnkiDroid/src/main/AndroidManifest.xml index ba8d6f6cc241..c77b5ecf0e5c 100644 --- a/AnkiDroid/src/main/AndroidManifest.xml +++ b/AnkiDroid/src/main/AndroidManifest.xml @@ -345,15 +345,6 @@ android:name=".multimedia.MultimediaActivity" android:configChanges="orientation|screenSize" android:exported="false" /> - - - - - (R.string.search_preference_key) - .searchConfiguration - .setFragmentContainerViewId((view.parent as? ViewGroup)?.id ?: R.id.settings_container) + requirePreference(R.string.search_preference_key).searchConfiguration + .setOnSearchListener { searchPreferenceFragment -> + parentFragmentManager.commit { + val containerId = (view.parent as? ViewGroup)?.id ?: R.id.settings_container + replace(containerId, searchPreferenceFragment) + addToBackStack(null) + } + } } companion object { - fun configureSearchBar(activity: AppCompatActivity, searchConfiguration: SearchConfiguration) { - val setDuePreferenceTitle = TR.actionsSetDueDate().toSentenceCase(activity, R.string.sentence_set_due_date) + fun configureSearchBar(context: Context, searchConfiguration: SearchConfiguration) { + val setDuePreferenceTitle = TR.actionsSetDueDate().toSentenceCase(context, R.string.sentence_set_due_date) with(searchConfiguration) { - setActivity(activity) setBreadcrumbsEnabled(true) setFuzzySearchEnabled(false) setHistoryEnabled(true) @@ -105,20 +112,20 @@ class HeaderFragment : PreferenceFragmentCompat(), TitleProvider { index(R.xml.preferences_reviewing) index(R.xml.preferences_sync) index(R.xml.preferences_custom_sync_server) - .addBreadcrumb(R.string.pref_cat_sync) + .addBreadcrumb(context.getString(R.string.pref_cat_sync)) index(R.xml.preferences_notifications) index(R.xml.preferences_appearance) index(R.xml.preferences_custom_buttons) - .addBreadcrumb(R.string.pref_cat_appearance) + .addBreadcrumb(context.getString(R.string.pref_cat_appearance)) index(R.xml.preferences_controls) index(R.xml.preferences_accessibility) index(R.xml.preferences_backup_limits) - ignorePreference(activity.getString(R.string.pref_backups_help_key)) + ignorePreference(context.getString(R.string.pref_backups_help_key)) indexItem() - .withKey(activity.getString(R.string.reschedule_command_key)) + .withKey(context.getString(R.string.reschedule_command_key)) .withTitle(setDuePreferenceTitle) .withResId(R.xml.preferences_controls) - .addBreadcrumb(activity.getString(R.string.pref_cat_controls)) + .addBreadcrumb(context.getString(R.string.pref_cat_controls)) .addBreadcrumb(setDuePreferenceTitle) } @@ -126,11 +133,11 @@ class HeaderFragment : PreferenceFragmentCompat(), TitleProvider { // so they should be searchable based on the same conditions /** From [HeaderFragment.onCreatePreferences] */ - if (DevOptionsFragment.isEnabled(activity)) { + if (DevOptionsFragment.isEnabled(context)) { searchConfiguration.index(R.xml.preferences_dev_options) /** From [DevOptionsFragment.initSubscreen] */ if (BuildConfig.DEBUG) { - searchConfiguration.ignorePreference(activity.getString(R.string.dev_options_enabled_by_user_key)) + searchConfiguration.ignorePreference(context.getString(R.string.dev_options_enabled_by_user_key)) } } @@ -141,16 +148,16 @@ class HeaderFragment : PreferenceFragmentCompat(), TitleProvider { /** From [NotificationsSettingsFragment.initSubscreen] */ if (AdaptionUtil.isXiaomiRestrictedLearningDevice) { - searchConfiguration.ignorePreference(activity.getString(R.string.pref_notifications_vibrate_key)) - searchConfiguration.ignorePreference(activity.getString(R.string.pref_notifications_blink_key)) + searchConfiguration.ignorePreference(context.getString(R.string.pref_notifications_vibrate_key)) + searchConfiguration.ignorePreference(context.getString(R.string.pref_notifications_blink_key)) } /** From [AdvancedSettingsFragment.removeUnnecessaryAdvancedPrefs] */ if (!CompatHelper.hasScrollKeys()) { - searchConfiguration.ignorePreference(activity.getString(R.string.double_scrolling_gap_key)) + searchConfiguration.ignorePreference(context.getString(R.string.double_scrolling_gap_key)) } - searchConfiguration.ignorePreference(activity.getString(R.string.user_actions_controls_category_key)) + searchConfiguration.ignorePreference(context.getString(R.string.user_actions_controls_category_key)) } /** diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt index 24d1fe420fb8..0ef318279dc8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/Preferences.kt @@ -30,7 +30,6 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.commit import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat -import com.bytehamster.lib.preferencesearch.SearchConfiguration import com.bytehamster.lib.preferencesearch.SearchPreferenceResult import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener import com.google.android.material.appbar.AppBarLayout @@ -113,7 +112,7 @@ class PreferencesFragment : override fun onSearchResultClicked(result: SearchPreferenceResult) { val fragment = getFragmentFromXmlRes(result.resourceFile) ?: return - parentFragmentManager.popBackStack() // clear the search fragment from the backstack + childFragmentManager.popBackStack() // clear the search fragment from the backstack childFragmentManager.commit { replace(R.id.settings_container, fragment, fragment.javaClass.name) addToBackStack(fragment.javaClass.name) @@ -154,29 +153,11 @@ class PreferencesFragment : replace(R.id.settings_container, initialFragment, initialFragment::class.java.name) } } -} - -/** - * Host activity for [PreferencesFragment]. - * - * Only necessary because [SearchConfiguration] demands an activity that implements - * [SearchPreferenceResultListener]. - */ -class PreferencesActivity : SingleFragmentActivity(), SearchPreferenceResultListener { - override fun onSearchResultClicked(result: SearchPreferenceResult) { - val fragment = supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) - if (fragment is SearchPreferenceResultListener) { - fragment.onSearchResultClicked(result) - } - } companion object { fun getIntent(context: Context, initialFragment: KClass? = null): Intent { val arguments = bundleOf(INITIAL_FRAGMENT_EXTRA to initialFragment?.jvmName) - return Intent(context, PreferencesActivity::class.java).apply { - putExtra(FRAGMENT_NAME_EXTRA, PreferencesFragment::class.jvmName) - putExtra(FRAGMENT_ARGS_EXTRA, arguments) - } + return SingleFragmentActivity.getIntent(context, PreferencesFragment::class, arguments) } } } diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/ActivityStartupUnderBackupTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/ActivityStartupUnderBackupTest.kt index 0d06d84828ad..3f006ab64c4a 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/ActivityStartupUnderBackupTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/ActivityStartupUnderBackupTest.kt @@ -18,7 +18,6 @@ package com.ichi2.anki import android.app.Activity import android.os.Looper.getMainLooper import com.ichi2.anki.instantnoteeditor.InstantNoteEditorActivity -import com.ichi2.anki.preferences.PreferencesActivity import com.ichi2.testutils.ActivityList import com.ichi2.testutils.ActivityList.ActivityLaunchParam import com.ichi2.testutils.EmptyApplication @@ -51,7 +50,6 @@ class ActivityStartupUnderBackupTest : RobolectricTest() { fun before() { notYetHandled(IntentHandler::class.java.simpleName, "Not working (or implemented) - inherits from Activity") notYetHandled(IntentHandler2::class.java.simpleName, "Not working (or implemented) - inherits from Activity") - notYetHandled(PreferencesActivity::class.java.simpleName, "Not working (or implemented) - inherits from AppCompatPreferenceActivity") notYetHandled(FilteredDeckOptions::class.java.simpleName, "Not working (or implemented) - inherits from AppCompatPreferenceActivity") notYetHandled(SingleFragmentActivity::class.java.simpleName, "Implemented, but the test fails because the activity throws if a specific intent extra isn't set") notYetHandled(InstantNoteEditorActivity::class.java.simpleName, "Single instance activity so should be used") diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/PreferencesTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/PreferencesTest.kt index 2879e1b4ea0a..0ec6b99137cb 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/PreferencesTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/PreferencesTest.kt @@ -15,25 +15,24 @@ */ package com.ichi2.anki.preferences -import android.content.Context -import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.fragment.app.commitNow import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.R import com.ichi2.anki.RobolectricTest +import com.ichi2.anki.SingleFragmentActivity import com.ichi2.anki.preferences.HeaderFragment.Companion.getHeaderKeyForFragment import com.ichi2.anki.preferences.PreferenceTestUtils.getAttrFromXml import com.ichi2.libanki.exception.ConfirmModSchemaException import com.ichi2.preferences.HeaderPreference -import com.ichi2.testutils.getJavaMethodAsAccessible import com.ichi2.utils.getInstanceFromClassName import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.robolectric.Robolectric import org.robolectric.annotation.Config import kotlin.reflect.jvm.jvmName import kotlin.test.assertEquals @@ -41,18 +40,14 @@ import kotlin.test.assertTrue @RunWith(AndroidJUnit4::class) class PreferencesTest : RobolectricTest() { - private lateinit var preferences: PreferencesActivity + private lateinit var preferences: SingleFragmentActivity @Before override fun setUp() { super.setUp() - preferences = PreferencesActivity() - val attachBaseContext = getJavaMethodAsAccessible( - AppCompatActivity::class.java, - "attachBaseContext", - Context::class.java - ) - attachBaseContext.invoke(preferences, targetContext) + val intent = PreferencesFragment.getIntent(targetContext) + preferences = Robolectric.buildActivity(SingleFragmentActivity::class.java, intent) + .create().start().resume().get() } @Test @@ -79,7 +74,7 @@ class PreferencesTest : RobolectricTest() { /** checks if any of the Preferences fragments throws while being created */ @Test fun fragmentsDoNotThrowOnCreation() { - val activityScenario = ActivityScenario.launch(PreferencesActivity.getIntent(targetContext)) + val activityScenario = ActivityScenario.launch(PreferencesFragment.getIntent(targetContext)) activityScenario.onActivity { activity -> PreferenceTestUtils.getAllPreferencesFragments(activity).forEach { diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/SettingsSearchBarTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/SettingsSearchBarTest.kt index cbe54c7efed7..f094add5ffe0 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/preferences/SettingsSearchBarTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/preferences/SettingsSearchBarTest.kt @@ -19,6 +19,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.bytehamster.lib.preferencesearch.PreferenceItem import com.bytehamster.lib.preferencesearch.SearchConfiguration import com.ichi2.anki.RobolectricTest +import com.ichi2.anki.SingleFragmentActivity import com.ichi2.testutils.getJavaFieldAsAccessible import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo @@ -35,11 +36,11 @@ class SettingsSearchBarTest : RobolectricTest() { fun `All indexed XML resIDs lead to the correct fragments on getFragmentFromXmlRes`() { // TODO try mocking the activity val preferencesActivity = getPreferencesActivity() - val searchConfig = SearchConfiguration(preferencesActivity) + val searchConfig = SearchConfiguration() HeaderFragment.configureSearchBar(preferencesActivity, searchConfig) // Use reflection to access some private fields - val filesToIndexField = getJavaFieldAsAccessible(SearchConfiguration::class.java, "filesToIndex") + val filesToIndexField = getJavaFieldAsAccessible(SearchConfiguration::class.java, "files") val searchItemResIdField = getJavaFieldAsAccessible(SearchConfiguration.SearchIndexItem::class.java, "resId") val preferencesToIndexField = getJavaFieldAsAccessible(SearchConfiguration::class.java, "preferencesToIndex") val prefItemResIdField = getJavaFieldAsAccessible(PreferenceItem::class.java, "resId") @@ -73,9 +74,9 @@ class SettingsSearchBarTest : RobolectricTest() { } } - private fun getPreferencesActivity(): PreferencesActivity { - val intent = PreferencesActivity.getIntent(targetContext) - return Robolectric.buildActivity(PreferencesActivity::class.java, intent) + private fun getPreferencesActivity(): SingleFragmentActivity { + val intent = PreferencesFragment.getIntent(targetContext) + return Robolectric.buildActivity(SingleFragmentActivity::class.java, intent) .create().start().resume().get() } } diff --git a/AnkiDroid/src/test/java/com/ichi2/testutils/ActivityList.kt b/AnkiDroid/src/test/java/com/ichi2/testutils/ActivityList.kt index 48da2b582864..5acd45213832 100644 --- a/AnkiDroid/src/test/java/com/ichi2/testutils/ActivityList.kt +++ b/AnkiDroid/src/test/java/com/ichi2/testutils/ActivityList.kt @@ -41,7 +41,6 @@ import com.ichi2.anki.StudyOptionsActivity import com.ichi2.anki.instantnoteeditor.InstantNoteEditorActivity import com.ichi2.anki.multimedia.MultimediaActivity import com.ichi2.anki.notetype.ManageNotetypes -import com.ichi2.anki.preferences.PreferencesActivity import com.ichi2.anki.previewer.CardViewerActivity import com.ichi2.anki.services.ReminderService.Companion.getReviewDeckIntent import com.ichi2.anki.ui.windows.managespace.ManageSpaceActivity @@ -74,7 +73,6 @@ object ActivityList { // Likely has unhandled intents get(Reviewer::class.java), get(MyAccount::class.java), - get(PreferencesActivity::class.java), get(FilteredDeckOptions::class.java), get(DrawingActivity::class.java), // Info has unhandled intents diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 789571f63b29..81c5eb0ca8fc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -95,7 +95,7 @@ okhttp = "4.12.0" protobufKotlinLite = "4.29.0" # ../AnkiDroid/robolectricDownload.gradle may need changes - read instructions in that file robolectric = "4.14.1" -searchpreference = "2.5.1" +searchpreference = "3.0.2" seismic = "1.0.3" sharedPreferencesMock = "1.2.4" slackKeeper = "0.16.1" @@ -158,7 +158,7 @@ android-lint-api = { module = "com.android.tools.lint:lint-api", version.ref = " android-lint = { module = "com.android.tools.lint:lint", version.ref = "lint" } android-lint-tests = { module = "com.android.tools.lint:lint-tests", version.ref = "lint" } protobuf-kotlin-lite = { module = "com.google.protobuf:protobuf-kotlin-lite", version.ref = "protobufKotlinLite" } -search-preference = { module = "com.github.ByteHamster:SearchPreference", version.ref = "searchpreference" } +search-preference = { module = "com.github.BrayanDSO:SearchPreference", version.ref = "searchpreference" } seismic = { module = "com.squareup:seismic", version.ref = "seismic" } slf4j-timber = { module = "com.arcao:slf4j-timber", version.ref = "slf4jTimber" } jakewharton-timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }