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,