Skip to content

Commit

Permalink
refactor: move most of the preferences activity into a fragment
Browse files Browse the repository at this point in the history
refactor: set fragment tag in SingleFragmentActivity

refactor: set back bar in preferences sw600dp layout

statically in the XML

refactor: set Preferences toolbar title in parent layout

Changing the title from child fragments isn't appropriate

refactor: simplify getAllCustomButtonKeys

refactor: inline HeaderFragment::setDevOptionsVisibility

refactor: replace hasLateralNavigation()

refactor: move Preferences companion content to top level

refactor: replace MINIMUM_CARDS_DUE_FOR_NOTIFICATION with Android resource

refactor: replace Preferences::setDevOptionsEnabled

removes DevOptionsFragment's dependency on Preferences

# Conflicts:
#	AnkiDroid/src/main/java/com/ichi2/anki/preferences/DevOptionsFragment.kt

refactor: inline restartWithNewDeckPicker

Used only once. Also, this refactor removes AdvancedSettingsFragment's dependency on Preferences

remove Preferences dependency in AboutFragment

Using recreate() forces HeaderFragment to be recreated and update the dev options header visibility

The snackbar was replaced with a toast because of the recreation. Since dev options is aimed at developers, the UX difference shouldn't matter

refactor: inline Preferences::hasAnkiWebAccount

used only once
  • Loading branch information
BrayanDSO committed Dec 3, 2024
1 parent 0c5ea9d commit 3d9246b
Show file tree
Hide file tree
Showing 26 changed files with 231 additions and 251 deletions.
2 changes: 1 addition & 1 deletion AnkiDroid/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@
android:configChanges="orientation|screenSize"
android:exported="false" />
<activity
android:name="com.ichi2.anki.preferences.Preferences"
android:name="com.ichi2.anki.preferences.PreferencesActivity"
android:exported="false"
android:configChanges="screenSize"
>
Expand Down
7 changes: 3 additions & 4 deletions AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,7 @@ import com.ichi2.anki.dialogs.DatabaseErrorDialog.DatabaseErrorDialogType
import com.ichi2.anki.dialogs.DialogHandler
import com.ichi2.anki.dialogs.SimpleMessageDialog
import com.ichi2.anki.dialogs.SimpleMessageDialog.SimpleMessageDialogListener
import com.ichi2.anki.preferences.Preferences
import com.ichi2.anki.preferences.Preferences.Companion.MINIMUM_CARDS_DUE_FOR_NOTIFICATION
import com.ichi2.anki.preferences.PENDING_NOTIFICATIONS_ONLY
import com.ichi2.anki.preferences.sharedPrefs
import com.ichi2.anki.receiver.SdCardReceiver
import com.ichi2.anki.snackbar.showSnackbar
Expand Down Expand Up @@ -527,8 +526,8 @@ open class AnkiActivity : AppCompatActivity, SimpleMessageDialogListener, Shortc
) {
val prefs = this.sharedPrefs()
// Show a notification unless all notifications have been totally disabled
if (prefs.getString(MINIMUM_CARDS_DUE_FOR_NOTIFICATION, "0")!!
.toInt() <= Preferences.PENDING_NOTIFICATIONS_ONLY
if (prefs.getString(getString(R.string.pref_notifications_minimum_cards_due_key), "0")!!
.toInt() <= PENDING_NOTIFICATIONS_ONLY
) {
// Use the title as the ticker unless the title is simply "AnkiDroid"
val ticker: String? = if (title == resources.getString(R.string.app_name)) {
Expand Down
3 changes: 1 addition & 2 deletions AnkiDroid/src/main/java/com/ichi2/anki/CollectionHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import androidx.core.content.edit
import com.ichi2.anki.AnkiDroidFolder.AppPrivateFolder
import com.ichi2.anki.exception.StorageAccessException
import com.ichi2.anki.exception.UnknownDatabaseVersionException
import com.ichi2.anki.preferences.Preferences
import com.ichi2.anki.preferences.sharedPrefs
import com.ichi2.libanki.Collection
import com.ichi2.libanki.DB
Expand All @@ -47,7 +46,7 @@ object CollectionHelper {
* This directory contains all AnkiDroid data and media for a given collection
* Except the Android preferences, cached files and [MetaDB]
*
* This can be changed by the [Preferences] screen
* This can be changed by the Preferences screen
* to allow a user to access a second collection via the same AnkiDroid app instance.
*
* The path also defines the collection that the AnkiDroid API accesses
Expand Down
3 changes: 2 additions & 1 deletion AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ import com.ichi2.anki.pages.AnkiPackageImporterFragment
import com.ichi2.anki.pages.CongratsPage
import com.ichi2.anki.pages.CongratsPage.Companion.onDeckCompleted
import com.ichi2.anki.preferences.AdvancedSettingsFragment
import com.ichi2.anki.preferences.PreferencesActivity
import com.ichi2.anki.preferences.sharedPrefs
import com.ichi2.anki.receiver.SdCardReceiver
import com.ichi2.anki.servicelayer.ScopedStorageService
Expand Down Expand Up @@ -784,7 +785,7 @@ open class DeckPicker :
convertDpToPixel(32F, this@DeckPicker).toInt()
)
positiveButton(R.string.open_settings) {
val settingsIntent = AdvancedSettingsFragment.getSubscreenIntent(this@DeckPicker)
val settingsIntent = PreferencesActivity.getIntent(this@DeckPicker, AdvancedSettingsFragment::class)
requestPathUpdateLauncher.launch(settingsIntent)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.color.MaterialColors
import com.google.android.material.navigation.NavigationView
import com.ichi2.anki.dialogs.help.HelpDialog
import com.ichi2.anki.preferences.Preferences
import com.ichi2.anki.preferences.PreferencesActivity
import com.ichi2.anki.preferences.sharedPrefs
import com.ichi2.anki.workarounds.FullDraggableContainerFix
import com.ichi2.compat.CompatHelper
Expand Down Expand Up @@ -357,10 +357,9 @@ abstract class NavigationDrawerActivity :

/**
* Opens AnkiDroid's Settings Screen.
* @see Preferences
*/
protected fun openSettings() {
val intent = Intent(this, Preferences::class.java)
val intent = PreferencesActivity.getIntent(this)
preferencesLauncher.launch(intent)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ open class SingleFragmentActivity : AnkiActivity(), CustomStudyDialog.CustomStud
arguments = intent.getBundleExtra(FRAGMENT_ARGS_EXTRA)
}
supportFragmentManager.commit {
replace(R.id.fragment_container, fragment)
replace(R.id.fragment_container, fragment, FRAGMENT_TAG)
}
}

Expand All @@ -100,6 +100,7 @@ open class SingleFragmentActivity : AnkiActivity(), CustomStudyDialog.CustomStud
companion object {
const val FRAGMENT_NAME_EXTRA = "fragmentName"
const val FRAGMENT_ARGS_EXTRA = "fragmentArgs"
const val FRAGMENT_TAG = "SingleFragmentActivityTag"

fun getIntent(context: Context, fragmentClass: KClass<out Fragment>, arguments: Bundle? = null, intentAction: String? = null): Intent {
return Intent(context, SingleFragmentActivity::class.java).apply {
Expand Down
30 changes: 13 additions & 17 deletions AnkiDroid/src/main/java/com/ichi2/anki/preferences/AboutFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.core.content.edit
import androidx.core.text.parseAsHtml
import androidx.fragment.app.Fragment
import com.ichi2.anki.AnkiActivity
Expand All @@ -34,7 +35,7 @@ import com.ichi2.anki.Info
import com.ichi2.anki.R
import com.ichi2.anki.launchCatchingTask
import com.ichi2.anki.servicelayer.DebugInfoService
import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.anki.showThemedToast
import com.ichi2.utils.IntentUtil
import com.ichi2.utils.VersionUtils.pkgVersionName
import com.ichi2.utils.copyToClipboard
Expand All @@ -46,7 +47,9 @@ import java.util.Date
import java.util.Locale
import net.ankiweb.rsdroid.BuildConfig as BackendBuildConfig

class AboutFragment : Fragment(R.layout.about_layout) {
class AboutFragment : Fragment(R.layout.about_layout), TitleProvider {
override val title: CharSequence
get() = getString(R.string.pref_cat_about_title)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// Version date
Expand All @@ -67,7 +70,7 @@ class AboutFragment : Fragment(R.layout.about_layout) {

// Logo secret
view.findViewById<ImageView>(R.id.about_app_logo)
.setOnClickListener(DevOptionsSecretClickListener(requireActivity() as Preferences))
.setOnClickListener(DevOptionsSecretClickListener(this))

// Contributors text
val contributorsLink = getString(R.string.link_contributors)
Expand Down Expand Up @@ -112,11 +115,6 @@ class AboutFragment : Fragment(R.layout.about_layout) {
}
}

override fun onStart() {
super.onStart()
requireActivity().setTitle(R.string.pref_cat_about_title)
}

/**
* Copies debug info (from [DebugInfoService.getDebugInfo]) to the clipboard
*/
Expand All @@ -136,7 +134,7 @@ class AboutFragment : Fragment(R.layout.about_layout) {
* Click listener which enables developer options on release builds
* if the user clicks it a minimum number of times
*/
private class DevOptionsSecretClickListener(val preferencesActivity: Preferences) : View.OnClickListener {
private class DevOptionsSecretClickListener(val fragment: Fragment) : View.OnClickListener {
private var clickCount = 0
private val clickLimit = 6

Expand All @@ -157,20 +155,18 @@ class AboutFragment : Fragment(R.layout.about_layout) {
setTitle(R.string.dev_options_enabled_pref)
setIcon(R.drawable.ic_warning)
setMessage(R.string.dev_options_warning)
setPositiveButton(R.string.dialog_ok) { _, _ -> enableDevOptions() }
setPositiveButton(R.string.dialog_ok) { _, _ -> enableDevOptions(context) }
setNegativeButton(R.string.dialog_cancel) { _, _ -> clickCount = 0 }
setCancelable(false)
}
}

/**
* Enables developer options for the user and shows it on [HeaderFragment]
*/
fun enableDevOptions() {
preferencesActivity.setDevOptionsEnabled(true)
preferencesActivity.showSnackbar(R.string.dev_options_enabled_msg) {
setAction(R.string.undo) { preferencesActivity.setDevOptionsEnabled(false) }
fun enableDevOptions(context: Context) {
context.sharedPrefs().edit {
putBoolean(context.getString(R.string.dev_options_enabled_by_user_key), true)
}
fragment.requireActivity().recreate()
showThemedToast(context, R.string.dev_options_enabled_msg, shortLength = true)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package com.ichi2.anki.preferences

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import androidx.appcompat.app.AlertDialog
Expand All @@ -25,9 +24,12 @@ import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat
import com.ichi2.anki.AnkiActivity
import com.ichi2.anki.CollectionHelper
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.DeckPicker
import com.ichi2.anki.MetaDB
import com.ichi2.anki.R
import com.ichi2.anki.exception.StorageAccessException
import com.ichi2.anki.launchCatchingTask
import com.ichi2.anki.provider.CardContentProvider
import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.compat.CompatHelper
Expand All @@ -49,7 +51,12 @@ class AdvancedSettingsFragment : SettingsFragment() {
val newPath = newValue as String
try {
CollectionHelper.initializeAnkiDroidDirectory(newPath)
(requireActivity() as Preferences).restartWithNewDeckPicker()
launchCatchingTask {
CollectionManager.discardBackend()
val deckPicker = Intent(requireContext(), DeckPicker::class.java)
deckPicker.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(deckPicker)
}
true
} catch (e: StorageAccessException) {
// TODO: Request MANAGE_EXTERNAL_STORAGE
Expand Down Expand Up @@ -134,10 +141,4 @@ class AdvancedSettingsFragment : SettingsFragment() {
}
}
}

companion object {
fun getSubscreenIntent(context: Context): Intent {
return getSubscreenIntent(context, AdvancedSettingsFragment::class)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
*/
package com.ichi2.anki.preferences

import android.content.Context
import android.content.Intent
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AlertDialog
import androidx.core.content.edit
Expand Down Expand Up @@ -59,10 +57,4 @@ class CustomButtonsSettingsFragment : SettingsFragment() {
fun allKeys(): HashSet<String> {
return allPreferences().mapTo(hashSetOf()) { it.key }
}

companion object {
fun getSubscreenIntent(context: Context): Intent {
return getSubscreenIntent(context, CustomButtonsSettingsFragment::class)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,12 @@ class DevOptionsFragment : SettingsFragment() {
* Destroys the fragment and hides developer options on [HeaderFragment]
*/
private fun disableDevOptions() {
(requireActivity() as Preferences).setDevOptionsEnabled(false)
// Update the "devOptionsEnabledByUser" pref value
AnkiDroidApp.sharedPrefs().edit {
putBoolean(getString(R.string.dev_options_enabled_by_user_key), false)
}
parentFragmentManager.popBackStack()
requireActivity().recreate()
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,18 @@ import com.ichi2.anki.CollectionManager
import com.ichi2.anki.CollectionManager.TR
import com.ichi2.anki.R
import com.ichi2.anki.ui.internationalization.toSentenceCase
import com.ichi2.anki.utils.isWindowCompact
import com.ichi2.compat.CompatHelper
import com.ichi2.preferences.HeaderPreference
import com.ichi2.utils.AdaptionUtil

class HeaderFragment : PreferenceFragmentCompat() {
class HeaderFragment : PreferenceFragmentCompat(), TitleProvider {
private var selectedHeaderPreference: HeaderPreference? = null
private var selectedHeaderPreferenceKey: String = DEFAULT_SELECTED_HEADER

override val title: CharSequence
get() = getString(R.string.settings)

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.preference_headers, rootKey)

Expand All @@ -51,9 +56,8 @@ class HeaderFragment : PreferenceFragmentCompat() {
}
}

if (DevOptionsFragment.isEnabled(requireContext())) {
setDevOptionsVisibility(true)
}
requirePreference<Preference>(R.string.pref_dev_options_screen_key)
.isVisible = DevOptionsFragment.isEnabled(requireContext())

configureSearchBar(
requireActivity() as AppCompatActivity,
Expand All @@ -62,7 +66,7 @@ class HeaderFragment : PreferenceFragmentCompat() {
}

private fun highlightHeaderPreference(headerPreference: HeaderPreference) {
if (!(activity as Preferences).hasLateralNavigation()) {
if (resources.isWindowCompact()) {
return
}
selectedHeaderPreference?.setHighlighted(false)
Expand All @@ -88,11 +92,6 @@ class HeaderFragment : PreferenceFragmentCompat() {
highlightHeaderPreference(requirePreference<HeaderPreference>(selectedHeaderPreferenceKey))
}

override fun onStart() {
super.onStart()
requireActivity().setTitle(R.string.settings)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// use the same fragment container to search in case there is a navigation container
Expand All @@ -101,10 +100,6 @@ class HeaderFragment : PreferenceFragmentCompat() {
.setFragmentContainerViewId((view.parent as? ViewGroup)?.id ?: R.id.settings_container)
}

fun setDevOptionsVisibility(isVisible: Boolean) {
requirePreference<Preference>(R.string.pref_dev_options_screen_key).isVisible = isVisible
}

companion object {
private const val KEY_SELECTED_HEADER_PREF = "selected_header_pref"
private const val DEFAULT_SELECTED_HEADER = "generalScreen"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class NotificationsSettingsFragment : SettingsFragment() {
updateNotificationPreference(this)
setOnPreferenceChangeListener { preference, newValue ->
updateNotificationPreference(preference as ListPreference)
if ((newValue as String).toInt() < Preferences.PENDING_NOTIFICATIONS_ONLY) {
if ((newValue as String).toInt() < PENDING_NOTIFICATIONS_ONLY) {
scheduleNotification(TimeManager.time, requireContext())
} else {
val intent = PendingIntentCompat.getBroadcast(
Expand Down
Loading

0 comments on commit 3d9246b

Please sign in to comment.