diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt
index c3005cbf5102..45017bfc778f 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt
@@ -117,7 +117,7 @@ open class CardBrowser :
}
override var fragmented: Boolean
- get() = viewModel.isFragmented
+ get() = Prefs.showSplitView && viewModel.isFragmented
set(_) {
throw UnsupportedOperationException()
}
@@ -235,7 +235,8 @@ open class CardBrowser :
*/
// TODO: Consider refactoring by storing noteEditorFrame and similar views in a sealed class (e.g., FragmentAccessor).
val fragmented =
- Prefs.devIsCardBrowserFragmented &&
+ Prefs.showSplitView &&
+ Prefs.devIsCardBrowserFragmented &&
!useSearchView &&
binding.noteEditorFrame?.visibility == View.VISIBLE
Timber.i("Using split Browser: %b", fragmented)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt
index a5f2a31ae2e1..d013f88d25a0 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt
@@ -218,7 +218,7 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi
tempNoteType = CardTemplateNotetype.fromBundle(savedInstanceState)
}
- fragmented = binding.fragmentContainer?.isVisible == true
+ fragmented = Prefs.showSplitView && binding.fragmentContainer?.isVisible == true
setNavigationBarColor(R.attr.alternativeBackgroundColor)
@@ -236,6 +236,9 @@ open class CardTemplateEditor : AnkiActivity(R.layout.activity_card_template_edi
leftPaneWeightKey = PREF_TEMPLATE_EDITOR_PANE_WEIGHT,
rightPaneWeightKey = PREF_TEMPLATE_PREVIEWER_PANE_WEIGHT,
)
+ } else {
+ binding.fragmentContainer?.isVisible = false
+ binding.cardTemplateEditorResizingDivider?.isVisible = false
}
// Open TemplatePreviewerFragment if in fragmented mode
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
index 5ed72983f2a6..6501ad184859 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
@@ -28,7 +28,6 @@ package com.ichi2.anki
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
-import android.content.res.Configuration
import android.database.SQLException
import android.graphics.Color
import android.graphics.PixelFormat
@@ -39,6 +38,7 @@ import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
import android.view.View
+import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
@@ -276,8 +276,8 @@ open class DeckPicker :
override var fragmented: Boolean
get() =
- resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK ==
- Configuration.SCREENLAYOUT_SIZE_XLARGE
+ Prefs.showSplitView &&
+ resources.configuration.screenWidthDp >= 840
set(_) = throw UnsupportedOperationException()
// Short animation duration from system
@@ -509,6 +509,18 @@ open class DeckPicker :
}
setViewBinding(binding)
+ if (!fragmented) {
+ binding.studyoptionsFragment?.isVisible = false
+ binding.resizingDivider?.isVisible = false
+ // Ensure the deck picker pane fills the full width
+ deckPickerBinding.root.let { pane ->
+ (pane.layoutParams as? android.widget.LinearLayout.LayoutParams)?.let {
+ it.width = ViewGroup.LayoutParams.MATCH_PARENT
+ it.weight = 0f
+ pane.layoutParams = it
+ }
+ }
+ }
enableToolbar()
// TODO This method is run on every activity recreation, which can happen often.
// It seems that the original idea was for for this to only run once, on app start.
@@ -770,7 +782,7 @@ open class DeckPicker :
}
fun onResizingDividerVisibilityChanged(isVisible: Boolean) {
- binding.resizingDivider?.isVisible = isVisible
+ binding.resizingDivider?.isVisible = isVisible && fragmented
}
fun onCardsDueChanged(dueCount: Int?) {
@@ -1946,6 +1958,11 @@ open class DeckPicker :
* @return whether the panel was shown
*/
private fun tryShowStudyOptionsPanel(): Boolean {
+ if (!fragmented) {
+ binding.studyoptionsFragment?.isVisible = false
+ binding.resizingDivider?.isVisible = false
+ return false
+ }
val containerId = binding.studyoptionsFragment?.id ?: return false
supportFragmentManager.commit {
replace(containerId, StudyOptionsFragment())
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt
index 0feb1f7c6f97..53c75edb9f63 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorActivity.kt
@@ -77,7 +77,7 @@ class NoteEditorActivity :
private var refreshPreviewerJob: Job? = null
val fragmented: Boolean
- get() = previewerFrame?.isVisible == true
+ get() = Prefs.showSplitView && previewerFrame?.isVisible == true
private lateinit var binding: ActivityNoteEditorBinding
@@ -94,6 +94,12 @@ class NoteEditorActivity :
setContentView(binding.root)
previewerFrame = binding.previewerFrame
+ // Hide the preview pane and divider when split view is disabled so the layout collapses
+ // to single-pane and the toolbar preview icon becomes available again.
+ if (previewerFrame != null && !Prefs.showSplitView) {
+ binding.previewerFrameLayout?.isVisible = false
+ binding.noteEditorResizingDivider?.isVisible = false
+ }
Timber.i("Note Editor is in %s mode", if (fragmented) "split" else "single-pane")
// TODO: specify how non-null but invalid extras are handled
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnalyticsConstants.kt b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnalyticsConstants.kt
index 85bf38d8f7ee..dc378383c6d0 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnalyticsConstants.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnalyticsConstants.kt
@@ -175,6 +175,7 @@ object AnalyticsConstants {
R.string.show_audio_play_buttons_key, // Show play buttons on cards with audio (reversed in collection: HIDE_AUDIO_PLAY_BUTTONS)
R.string.pref_display_filenames_in_browser_key, // Display filenames in card browser
R.string.show_deck_title_key, // Show deck title
+ R.string.show_split_view_key, // Show split view
// ******************************** Controls *********************************************
R.string.gestures_preference, // Enable gestures
R.string.gestures_corner_touch_preference, // 9-point touch
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt b/AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt
index b50346a51671..a124a8b67479 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/settings/Prefs.kt
@@ -240,6 +240,13 @@ open class PrefsRepository(
val exitViaDoubleTapBack by booleanPref(R.string.exit_via_double_tap_back_key, false)
+ /**
+ * Whether to show side-by-side split panes on large screens.
+ * Applies to the note editor, card browser, and template editor.
+ * When false, all screens use a single-pane layout.
+ */
+ var showSplitView by booleanPref(R.string.show_split_view_key, defaultValue = true)
+
// ****************************************** Sync ****************************************** //
val isAutoSyncEnabled by booleanPref(R.string.automatic_sync_choice_key, false)
diff --git a/AnkiDroid/src/main/res/layout-sw600dp/activity_card_browser.xml b/AnkiDroid/src/main/res/layout-w840dp/activity_card_browser.xml
similarity index 100%
rename from AnkiDroid/src/main/res/layout-sw600dp/activity_card_browser.xml
rename to AnkiDroid/src/main/res/layout-w840dp/activity_card_browser.xml
diff --git a/AnkiDroid/src/main/res/layout-sw600dp/activity_card_template_editor.xml b/AnkiDroid/src/main/res/layout-w840dp/activity_card_template_editor.xml
similarity index 100%
rename from AnkiDroid/src/main/res/layout-sw600dp/activity_card_template_editor.xml
rename to AnkiDroid/src/main/res/layout-w840dp/activity_card_template_editor.xml
diff --git a/AnkiDroid/src/main/res/layout-sw600dp/activity_homescreen.xml b/AnkiDroid/src/main/res/layout-w840dp/activity_homescreen.xml
similarity index 100%
rename from AnkiDroid/src/main/res/layout-sw600dp/activity_homescreen.xml
rename to AnkiDroid/src/main/res/layout-w840dp/activity_homescreen.xml
diff --git a/AnkiDroid/src/main/res/layout-sw600dp/activity_note_editor.xml b/AnkiDroid/src/main/res/layout-w840dp/activity_note_editor.xml
similarity index 100%
rename from AnkiDroid/src/main/res/layout-sw600dp/activity_note_editor.xml
rename to AnkiDroid/src/main/res/layout-w840dp/activity_note_editor.xml
diff --git a/AnkiDroid/src/main/res/values/10-preferences.xml b/AnkiDroid/src/main/res/values/10-preferences.xml
index 1c2aaaef6656..73896908ffc1 100644
--- a/AnkiDroid/src/main/res/values/10-preferences.xml
+++ b/AnkiDroid/src/main/res/values/10-preferences.xml
@@ -34,6 +34,7 @@
Workarounds
Plugins
Editing
+ Layout
Stroke width
@@ -179,6 +180,8 @@
Accessibility
Paste clipboard images as PNG
+ Split view
+ Show side-by-side panes on large screens in the note editor, card browser, template editor, and deck picker
Press back twice to go back/exit
To avoid accidentally leaving the study screen or the app
diff --git a/AnkiDroid/src/main/res/values/preferences.xml b/AnkiDroid/src/main/res/values/preferences.xml
index 4d3f371d4c80..fc205dc98a9a 100644
--- a/AnkiDroid/src/main/res/values/preferences.xml
+++ b/AnkiDroid/src/main/res/values/preferences.xml
@@ -33,6 +33,7 @@
backupLimitsScreen
pastePNG
+ showSplitView
switchProfileScreen
enableSwitchProfile
diff --git a/AnkiDroid/src/main/res/xml/preferences_appearance.xml b/AnkiDroid/src/main/res/xml/preferences_appearance.xml
index 78647bc228ea..ce91fe9da0c9 100644
--- a/AnkiDroid/src/main/res/xml/preferences_appearance.xml
+++ b/AnkiDroid/src/main/res/xml/preferences_appearance.xml
@@ -128,6 +128,13 @@
android:defaultValue="false"
android:title="@string/show_deck_title"/>
+
+
+
= listOf("normal", "xlarge")
+ fun initParameters(): Collection = listOf("normal", "w1280dp")
}
@Before
@@ -537,7 +537,7 @@ class DeckPickerTest : RobolectricTest() {
@Test
fun checkDisplayOfStudyOptionsOnTablet() {
- assumeTrue("We are running on a tablet", qualifiers!!.contains("xlarge"))
+ assumeTrue("We are running on a tablet", qualifiers!!.contains("w1280dp"))
val deckPickerEx =
super.startActivityNormallyOpenCollectionWithIntent(
DeckPickerEx::class.java,
diff --git a/AnkiDroid/src/test/java/com/ichi2/testutils/ScreenSize.kt b/AnkiDroid/src/test/java/com/ichi2/testutils/ScreenSize.kt
index fdc1f3879197..f77669ba5f61 100644
--- a/AnkiDroid/src/test/java/com/ichi2/testutils/ScreenSize.kt
+++ b/AnkiDroid/src/test/java/com/ichi2/testutils/ScreenSize.kt
@@ -20,7 +20,7 @@ import org.robolectric.RuntimeEnvironment
import timber.log.Timber
/** [block] runs with a runtime qualifier emulating a split-pane display */
-fun withSplitPaneUi(block: () -> Unit) = withQualifier("sw700dp", block)
+fun withSplitPaneUi(block: () -> Unit) = withQualifier("w840dp", block)
fun withQualifier(
newQualifier: String,
@@ -38,7 +38,7 @@ fun withQualifier(
}
/** [block] runs with a runtime qualifier emulating a split-pane display */
-suspend fun withSplitPaneUiAsync(block: suspend () -> Unit) = withQualifierAsync("sw700dp", block)
+suspend fun withSplitPaneUiAsync(block: suspend () -> Unit) = withQualifierAsync("w840dp", block)
suspend fun withQualifierAsync(
newQualifier: String,