From 6e5b570f21753adaae9a9a5c08b5799c00e02d7f Mon Sep 17 00:00:00 2001 From: Robozinho <65715921+RobozinhoD@users.noreply.github.com> Date: Sat, 17 Aug 2024 14:23:47 -0300 Subject: [PATCH] fix: template previewer UI the template previewer was tightly coupled with the template editor, and that isn't a good design. To solve that, I decoupled the template previewer in two fragments: a view and a standalone page. All of the original style of the template previewer was fixed. I gave credits to BrayanDSO in TemplatePreviewerPage.kt because most of the code is his and I don't want the credits myself I also removed the duplicated TabLayout in the card editor because it didn't make sense to me. --- .../java/com/ichi2/anki/CardTemplateEditor.kt | 19 +++-- .../main/java/com/ichi2/anki/NoteEditor.kt | 4 +- .../anki/previewer/CardViewerActivity.kt | 3 +- .../anki/previewer/CardViewerFragment.kt | 2 +- .../previewer/TemplatePreviewerFragment.kt | 79 +---------------- .../anki/previewer/TemplatePreviewerPage.kt | 85 +++++++++++++++++++ .../previewer/TemplatePreviewerViewModel.kt | 3 +- .../layout-xlarge/card_template_editor.xml | 46 ++++++---- .../main/res/layout/card_template_editor.xml | 16 +++- .../layout/card_template_editor_activity.xml | 35 -------- .../res/layout/card_template_editor_main.xml | 14 +++ .../res/layout/card_template_editor_top.xml | 27 ++++++ .../main/res/layout/template_previewer.xml | 77 ++++++----------- .../layout/template_previewer_container.xml | 51 +++++++++++ 14 files changed, 268 insertions(+), 193 deletions(-) create mode 100644 AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerPage.kt delete mode 100644 AnkiDroid/src/main/res/layout/card_template_editor_activity.xml create mode 100644 AnkiDroid/src/main/res/layout/card_template_editor_main.xml create mode 100644 AnkiDroid/src/main/res/layout/card_template_editor_top.xml create mode 100644 AnkiDroid/src/main/res/layout/template_previewer_container.xml diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt index 5932afe9cd0c..1d322d9a00b3 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt @@ -38,6 +38,7 @@ import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.CheckResult import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting +import androidx.appcompat.widget.ThemeUtils import androidx.core.view.MenuHost import androidx.core.view.MenuProvider import androidx.core.view.ViewCompat @@ -45,7 +46,7 @@ import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentContainerView -import androidx.fragment.app.commit +import androidx.fragment.app.commitNow import androidx.lifecycle.Lifecycle import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 @@ -65,6 +66,7 @@ import com.ichi2.anki.notetype.RenameCardTemplateDialog import com.ichi2.anki.notetype.RepositionCardTemplateDialog import com.ichi2.anki.previewer.TemplatePreviewerArguments import com.ichi2.anki.previewer.TemplatePreviewerFragment +import com.ichi2.anki.previewer.TemplatePreviewerPage import com.ichi2.anki.snackbar.showSnackbar import com.ichi2.anki.utils.ext.isImageOcclusion import com.ichi2.anki.utils.postDelayed @@ -160,7 +162,7 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener { tempModel = CardTemplateNotetype.fromBundle(savedInstanceState) } - templatePreviewerFrame = findViewById(R.id.template_previewer_fragment) + templatePreviewerFrame = findViewById(R.id.fragment_container) /** * Check if templatePreviewerFrame is not null and if its visibility is set to VISIBLE. * If both conditions are true, assign true to the variable [fragmented], otherwise assign false. @@ -198,13 +200,14 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener { ord = ord, fields = note.fields, tags = note.tags, - fillEmpty = true, - inFragmentedActivity = fragmented + fillEmpty = true ) - val details = TemplatePreviewerFragment.newInstance(args) - supportFragmentManager.commit { - replace(R.id.template_previewer_fragment, details) + val fragment = TemplatePreviewerFragment.newInstance(args) + supportFragmentManager.commitNow { + replace(R.id.fragment_container, fragment) } + val backgroundColor = ThemeUtils.getThemeAttrColor(this@CardTemplateEditor, R.attr.alternativeBackgroundColor) + fragment.requireView().setBackgroundColor(backgroundColor) } } @@ -898,7 +901,7 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener { tags = note.tags, fillEmpty = true ) - val intent = TemplatePreviewerFragment.getIntent(requireContext(), args) + val intent = TemplatePreviewerPage.getIntent(requireContext(), args) startActivity(intent) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt index 17dfdaafadaa..4666a5466998 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt @@ -119,7 +119,7 @@ import com.ichi2.anki.noteeditor.Toolbar.TextWrapper import com.ichi2.anki.pages.ImageOcclusion import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.anki.previewer.TemplatePreviewerArguments -import com.ichi2.anki.previewer.TemplatePreviewerFragment +import com.ichi2.anki.previewer.TemplatePreviewerPage import com.ichi2.anki.receiver.SdCardReceiver import com.ichi2.anki.servicelayer.LanguageHintService import com.ichi2.anki.servicelayer.NoteService @@ -1433,7 +1433,7 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su ord = ord, fillEmpty = false ) - val intent = TemplatePreviewerFragment.getIntent(requireContext(), args) + val intent = TemplatePreviewerPage.getIntent(requireContext(), args) startActivity(intent) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerActivity.kt index 2a55dff64cd8..206adb3583ad 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerActivity.kt @@ -20,6 +20,7 @@ import android.content.Intent import android.os.Bundle import androidx.activity.enableEdgeToEdge import androidx.appcompat.widget.ThemeUtils +import androidx.fragment.app.Fragment import com.ichi2.anki.R import com.ichi2.anki.SingleFragmentActivity import com.ichi2.anki.utils.navBarNeedsScrim @@ -41,7 +42,7 @@ class CardViewerActivity : SingleFragmentActivity() { } companion object { - fun getIntent(context: Context, fragmentClass: KClass, arguments: Bundle? = null): Intent { + fun getIntent(context: Context, fragmentClass: KClass, arguments: Bundle? = null): Intent { return Intent(context, CardViewerActivity::class.java).apply { putExtra(FRAGMENT_NAME_EXTRA, fragmentClass.jvmName) putExtra(FRAGMENT_ARGS_EXTRA, arguments) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerFragment.kt index c69ce62c9334..18d09953e19c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/CardViewerFragment.kt @@ -49,7 +49,7 @@ import kotlinx.coroutines.flow.onEach import timber.log.Timber abstract class CardViewerFragment(@LayoutRes layout: Int) : Fragment(layout) { - protected abstract val viewModel: CardViewerViewModel + abstract val viewModel: CardViewerViewModel protected abstract val webView: WebView @CallSuper diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerFragment.kt index c9d4f8e428fb..dbf185a6847c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerFragment.kt @@ -15,23 +15,15 @@ */ package com.ichi2.anki.previewer -import android.content.Context -import android.content.Intent import android.os.Bundle -import android.view.MenuItem import android.view.View import android.webkit.WebView -import androidx.appcompat.widget.ThemeUtils -import androidx.appcompat.widget.Toolbar import androidx.core.os.BundleCompat import androidx.core.os.bundleOf import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.google.android.material.button.MaterialButton import com.google.android.material.card.MaterialCardView -import com.google.android.material.tabs.TabLayout -import com.google.android.material.tabs.TabLayout.OnTabSelectedListener -import com.ichi2.anki.CardTemplateEditor import com.ichi2.anki.R import com.ichi2.anki.cardviewer.CardMediaPlayer import com.ichi2.anki.snackbar.BaseSnackbarBuilderProvider @@ -39,23 +31,14 @@ import com.ichi2.anki.snackbar.SnackbarBuilder import com.ichi2.anki.utils.ext.sharedPrefs import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import timber.log.Timber class TemplatePreviewerFragment : CardViewerFragment(R.layout.template_previewer), - BaseSnackbarBuilderProvider, - Toolbar.OnMenuItemClickListener { - /** - * Whether this is displayed in a fragment view. - * If true, this fragment is on the trailing side of the card template editor. - */ - private var inFragmentedActivity = false - private lateinit var templatePreviewerArguments: TemplatePreviewerArguments + BaseSnackbarBuilderProvider { override val viewModel: TemplatePreviewerViewModel by viewModels { - templatePreviewerArguments = BundleCompat.getParcelable(requireArguments(), ARGS_KEY, TemplatePreviewerArguments::class.java)!! - TemplatePreviewerViewModel.factory(templatePreviewerArguments, CardMediaPlayer()) + val arguments = BundleCompat.getParcelable(requireArguments(), ARGS_KEY, TemplatePreviewerArguments::class.java)!! + TemplatePreviewerViewModel.factory(arguments, CardMediaPlayer()) } override val webView: WebView get() = requireView().findViewById(R.id.webview) @@ -66,19 +49,6 @@ class TemplatePreviewerFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val toolbar = view.findViewById(R.id.toolbar) - // Retrieve the boolean argument "inFragmentedActivity" from the fragment's arguments bundle - inFragmentedActivity = templatePreviewerArguments.inFragmentedActivity - // If this fragment is a part of fragmented activity, then there is already a navigation icon an the activity. - // We hide the one specific to this fragment - if (inFragmentedActivity) { - toolbar.navigationIcon = null - } else { - toolbar.setNavigationOnClickListener { - requireActivity().onBackPressedDispatcher.onBackPressed() - } - } - val showAnswerButton = view.findViewById(R.id.show_answer).apply { setOnClickListener { viewModel.toggleShowAnswer() } } @@ -92,44 +62,9 @@ class TemplatePreviewerFragment : } .launchIn(lifecycleScope) - val tabLayout = view.findViewById(R.id.tab_layout) - lifecycleScope.launch { - for (templateName in viewModel.getTemplateNames()) { - tabLayout.addTab(tabLayout.newTab().setText(templateName)) - } - tabLayout.selectTab(tabLayout.getTabAt(viewModel.getCurrentTabIndex())) - tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab) { - Timber.v("Selected tab %d", tab.position) - viewModel.onTabSelected(tab.position) - } - - override fun onTabUnselected(tab: TabLayout.Tab) { - // do nothing - } - - override fun onTabReselected(tab: TabLayout.Tab) { - // do nothing - } - }) - } - if (sharedPrefs().getBoolean("safeDisplay", false)) { view.findViewById(R.id.webview_container).elevation = 0F } - - with(requireActivity()) { - window.statusBarColor = ThemeUtils.getThemeAttrColor(this, R.attr.appBarColor) - } - if (inFragmentedActivity) { - toolbar.setOnMenuItemClickListener(this) - toolbar.inflateMenu(R.menu.card_template_editor) - (activity as CardTemplateEditor).currentFragment?.setupCommonMenu(toolbar.menu) - } - } - - override fun onMenuItemClick(item: MenuItem): Boolean { - return (activity as CardTemplateEditor).currentFragment?.handleCommonMenuItemSelected(item) == true } companion object { @@ -140,13 +75,5 @@ class TemplatePreviewerFragment : this.arguments = bundleOf(ARGS_KEY to arguments) } } - - fun getIntent(context: Context, arguments: TemplatePreviewerArguments): Intent { - return CardViewerActivity.getIntent( - context, - TemplatePreviewerFragment::class, - bundleOf(ARGS_KEY to arguments) - ) - } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerPage.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerPage.kt new file mode 100644 index 000000000000..2c745d423194 --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerPage.kt @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024 Brayan Oliveira + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.ichi2.anki.previewer + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.view.View +import androidx.core.os.BundleCompat +import androidx.core.os.bundleOf +import androidx.fragment.app.Fragment +import androidx.fragment.app.commitNow +import androidx.lifecycle.lifecycleScope +import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayout.OnTabSelectedListener +import com.ichi2.anki.R +import com.ichi2.anki.previewer.TemplatePreviewerFragment.Companion.ARGS_KEY +import kotlinx.coroutines.launch +import timber.log.Timber + +/** + * Container for [TemplatePreviewerFragment] that works as a standalone page + * by including a toolbar and a TabLayout for changing the current template. + */ +class TemplatePreviewerPage : Fragment(R.layout.template_previewer_container) { + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + view.findViewById(R.id.toolbar).setNavigationOnClickListener { + requireActivity().onBackPressedDispatcher.onBackPressed() + } + + val arguments = BundleCompat.getParcelable(requireArguments(), ARGS_KEY, TemplatePreviewerArguments::class.java)!! + val fragment = TemplatePreviewerFragment.newInstance(arguments) + childFragmentManager.commitNow { + replace(R.id.fragment_container, fragment) + } + + val viewModel = fragment.viewModel + val tabLayout = view.findViewById(R.id.tab_layout) + + lifecycleScope.launch { + for (templateName in viewModel.getTemplateNames()) { + tabLayout.addTab(tabLayout.newTab().setText(templateName)) + } + tabLayout.selectTab(tabLayout.getTabAt(viewModel.getCurrentTabIndex())) + tabLayout.addOnTabSelectedListener(object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + Timber.v("Selected tab %d", tab.position) + viewModel.onTabSelected(tab.position) + } + + override fun onTabUnselected(tab: TabLayout.Tab) { + // do nothing + } + + override fun onTabReselected(tab: TabLayout.Tab) { + // do nothing + } + }) + } + } + + companion object { + fun getIntent(context: Context, arguments: TemplatePreviewerArguments): Intent { + return CardViewerActivity.getIntent( + context, + TemplatePreviewerPage::class, + bundleOf(ARGS_KEY to arguments) + ) + } + } +} diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerViewModel.kt index 1e87bc79d625..0aa06fe921d9 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/TemplatePreviewerViewModel.kt @@ -237,8 +237,7 @@ data class TemplatePreviewerArguments( val tags: MutableList, val id: Long = 0, val ord: Int = 0, - val fillEmpty: Boolean = false, - val inFragmentedActivity: Boolean = false + val fillEmpty: Boolean = false ) : Parcelable { val notetype: NotetypeJson get() = notetypeFile.getNotetype() } diff --git a/AnkiDroid/src/main/res/layout-xlarge/card_template_editor.xml b/AnkiDroid/src/main/res/layout-xlarge/card_template_editor.xml index d981dc759f97..32fdeac071e1 100644 --- a/AnkiDroid/src/main/res/layout-xlarge/card_template_editor.xml +++ b/AnkiDroid/src/main/res/layout-xlarge/card_template_editor.xml @@ -1,22 +1,38 @@ - + + - + android:layout_height="match_parent"> + + + + + - + android:id="@+id/fragment_container" + android:layout_height="0dp" + android:layout_width="0dp" + app:layout_constraintTop_toBottomOf="@id/template_editor_top" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintStart_toEndOf="@id/template_editor" + app:layout_constraintEnd_toEndOf="parent" + /> + + + diff --git a/AnkiDroid/src/main/res/layout/card_template_editor.xml b/AnkiDroid/src/main/res/layout/card_template_editor.xml index 3936d7ad04b8..72fccca9ce78 100644 --- a/AnkiDroid/src/main/res/layout/card_template_editor.xml +++ b/AnkiDroid/src/main/res/layout/card_template_editor.xml @@ -1,6 +1,18 @@ - + android:layout_height="match_parent" + android:id="@+id/root_layout"> + + + + + + + + + \ No newline at end of file diff --git a/AnkiDroid/src/main/res/layout/card_template_editor_activity.xml b/AnkiDroid/src/main/res/layout/card_template_editor_activity.xml deleted file mode 100644 index 88e225a2332f..000000000000 --- a/AnkiDroid/src/main/res/layout/card_template_editor_activity.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - diff --git a/AnkiDroid/src/main/res/layout/card_template_editor_main.xml b/AnkiDroid/src/main/res/layout/card_template_editor_main.xml new file mode 100644 index 000000000000..b987c339d7ec --- /dev/null +++ b/AnkiDroid/src/main/res/layout/card_template_editor_main.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/AnkiDroid/src/main/res/layout/card_template_editor_top.xml b/AnkiDroid/src/main/res/layout/card_template_editor_top.xml new file mode 100644 index 000000000000..2325ed9028a4 --- /dev/null +++ b/AnkiDroid/src/main/res/layout/card_template_editor_top.xml @@ -0,0 +1,27 @@ + + + + + + + \ No newline at end of file diff --git a/AnkiDroid/src/main/res/layout/template_previewer.xml b/AnkiDroid/src/main/res/layout/template_previewer.xml index 93b63c972bbb..1ecc0b7f87b1 100644 --- a/AnkiDroid/src/main/res/layout/template_previewer.xml +++ b/AnkiDroid/src/main/res/layout/template_previewer.xml @@ -1,62 +1,37 @@ - + android:layout_height="match_parent"> - - - - - + android:layout_height="match_parent" + /> - - - - - + - + - - \ No newline at end of file + \ No newline at end of file diff --git a/AnkiDroid/src/main/res/layout/template_previewer_container.xml b/AnkiDroid/src/main/res/layout/template_previewer_container.xml new file mode 100644 index 000000000000..2a631e93b0f5 --- /dev/null +++ b/AnkiDroid/src/main/res/layout/template_previewer_container.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file