Skip to content

feat: Add wiring and analytics for bottom nav bar#21274

Open
ShaanNarendran wants to merge 5 commits into
ankidroid:mainfrom
ShaanNarendran:feat/wire-deck-picker-bottom-nav
Open

feat: Add wiring and analytics for bottom nav bar#21274
ShaanNarendran wants to merge 5 commits into
ankidroid:mainfrom
ShaanNarendran:feat/wire-deck-picker-bottom-nav

Conversation

@ShaanNarendran

@ShaanNarendran ShaanNarendran commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Note

Assisted by Opus 4.6 for general research.

Purpose / Description

This PR adds wiring to the bottom nav bar instantiated in the previous PRs (#21186). It also adds analytics to the More fragment.

Fixes

  • For GSoC 2026: Redesign Deck Picker

Approach

The main changes lie in DeckPicker.kt where setupBottomNav() method shows the bar, disables the drawer swipe, and handles tab selection. Each tab hides the deck list content and shows the corresponding fragment using show/hide so we can have state preservation. The CardBrowserFragment's ViewModel is lazily created the first time that the browser tab is tapped since otherwise the app would crash. There were some padding changed to be done to the fab to account for the nav bar height. Minor bugs will be fixed in a follow-up refinement PR along with removing the nav drawer altogether, I've kept it usable for now as a fallback but that will be removed.

How Has This Been Tested?

Compiles successfully, all the features work as intended.
Screen_recording_20260612_120332.webm

Checklist

Please, go through these checks before submitting the PR.

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
  • UI Changes: You have tested your change using the Google Accessibility Scanner

@ShaanNarendran ShaanNarendran added the GSoC Pull requests authored by a Google Summer of Code participant [Candidate/Selected], for GSoC mentors label Jun 15, 2026
@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch 2 times, most recently from 208da28 to 781fde8 Compare June 15, 2026 11:33
@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch from 781fde8 to 5ccbb46 Compare June 15, 2026 11:52

@sanjaysargam sanjaysargam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct the PR description, tests are not in this diff. If coming in the follow-up, say so explicitly.

Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated

@criticalAY criticalAY left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the icons in bottom bar and the text always be visible? see how Playstore has it (the icons are animated there but we can ignore that for now)

@criticalAY criticalAY added the Needs Author Reply Waiting for a reply from the original author label Jun 18, 2026
@ShaanNarendran

Copy link
Copy Markdown
Contributor Author

Can the icons in bottom bar and the text always be visible? see how Playstore has it (the icons are animated there but we can ignore that for now)

Sure, I should be able to add text. will have all the fixes out tomorrow, I was making sure it works for the current card browser too

@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch 2 times, most recently from d503153 to 3ac1f0b Compare June 21, 2026 06:02
@ShaanNarendran ShaanNarendran removed Needs Author Reply Waiting for a reply from the original author Has Conflicts labels Jun 21, 2026

@david-allison david-allison left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Thanks so much!!

Comment thread AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserFragment.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
@david-allison david-allison added the Needs Author Reply Waiting for a reply from the original author label Jun 21, 2026
@sanjaysargam

Copy link
Copy Markdown
Member

@ShaanNarendran, I noticed that the toolbar remains visible in CardBrowser, and there's a small visual glitch when navigating from CardBrowser to Statistics. Also, I think the status bar behavior should remain consistent across all top-level screens.
bottom.webm

@sanjaysargam sanjaysargam left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great progress 👍

Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt Outdated
@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch 2 times, most recently from dfb81c6 to 4cac40b Compare June 22, 2026 08:37
@ShaanNarendran

Copy link
Copy Markdown
Contributor Author

@ShaanNarendran, I noticed that the toolbar remains visible in CardBrowser, and there's a small visual glitch when navigating from CardBrowser to Statistics. Also, I think the status bar behavior should remain consistent across all top-level screens. bottom.webm

Could you pull the latest and see if this issue persists? Seems to work on my emulator

@ShaanNarendran ShaanNarendran removed Needs Author Reply Waiting for a reply from the original author Has Conflicts labels Jun 22, 2026
@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch from 291ebf3 to 1a2fb70 Compare June 22, 2026 08:46
@sanjaysargam

Copy link
Copy Markdown
Member

@ShaanNarendran

toolbar remains visible in CardBrowser and glitch one

This one seems resolved

But status bar consistency is still there in my case. Did you test on Light Theme?

@ShaanNarendran

ShaanNarendran commented Jun 23, 2026

Copy link
Copy Markdown
Contributor Author

But status bar consistency is still there in my case. Did you test on Light Theme?

By status bar consistency do you mean what is on top in each destination? Since every destination has their own unique toolbars I'm using those, if we wanted it to be uniform I'd have to recreate the top bars for each destination. But I may be understanding the issue wrong so if you could clarify I'll test it out again

@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch from 36ae184 to 1ec0e7e Compare June 26, 2026 08:50
@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch 2 times, most recently from 49b4e63 to a4f62ed Compare June 26, 2026 08:57
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt Outdated
Comment thread AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt Outdated

@criticalAY criticalAY left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is something that might help:

using show/hide to keep the fragments alive is the right call for state preservation The first-open lag is a side effect of that design rather than a bug, but worth a note for the follow-up.

soooo..
Each destination is created lazily on its first tap: showBottomNavFragment(...) only takes the add(...) path the first time, so the whole fragment lifecycle (onCreateView inflation + onViewCreated setup) runs on the main thread, you might want to explore more here but can be done later

Subject: [PATCH] bottom nav changes
---
Index: AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
--- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt	(revision a4f62edda816b0217f907d1232c64d36a666d8f4)
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt	(date 1782502339704)
@@ -2246,7 +2246,13 @@
             )
 
     companion object {
-        /** Material 3 BottomNavigationView standard height in dp */
+        /**
+         * Material 3 BottomNavigationView content height in dp, *excluding* the system
+         * navigation-bar inset (that inset is added separately here via `bars.bottom`).
+         * Used to offset the FAB above the bar. The hosted-fragment container instead uses
+         * the bar's measured height, which already includes the inset so the two must not
+         * be swapped for one another.
+         */
         private const val BOTTOM_NAV_HEIGHT_DP = 80
 
         /**
Index: AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt b/AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt
--- a/AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt	(revision a4f62edda816b0217f907d1232c64d36a666d8f4)
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/HomeScreenNavigation.kt	(date 1782502243528)
@@ -10,6 +10,7 @@
 import androidx.core.view.isVisible
 import androidx.core.view.updatePadding
 import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentTransaction
 import androidx.fragment.app.commit
 import androidx.lifecycle.ViewModelProvider
 import com.google.android.material.bottomnavigation.BottomNavigationView
@@ -69,11 +70,7 @@
     when (item) {
         NavigationItem.HOME -> {
             fragmentContainer.isVisible = false
-            deckPicker.supportFragmentManager.commit {
-                deckPicker.supportFragmentManager.fragments.forEach { frag ->
-                    if (frag.id == R.id.bottom_nav_fragment_container) hide(frag)
-                }
-            }
+            deckPicker.supportFragmentManager.commit { hideBottomNavFragments() }
             contentWrapper.isVisible = true
             deckPicker.floatingActionMenu.showFloatingActionButton()
             backCallback.isEnabled = false
@@ -82,17 +79,17 @@
         NavigationItem.BROWSER -> {
             backCallback.isEnabled = true
             ensureBrowserViewModel()
-            showBottomNavFragment(CardBrowserFragment(), "browser", contentWrapper, fragmentContainer)
+            showBottomNavFragment(::CardBrowserFragment, item.tag, contentWrapper, fragmentContainer)
             true
         }
         NavigationItem.STATS -> {
             backCallback.isEnabled = true
-            showBottomNavFragment(Statistics(), "stats", contentWrapper, fragmentContainer)
+            showBottomNavFragment(::Statistics, item.tag, contentWrapper, fragmentContainer)
             true
         }
         NavigationItem.MORE -> {
             backCallback.isEnabled = true
-            showBottomNavFragment(MoreFragment(), "more", contentWrapper, fragmentContainer)
+            showBottomNavFragment(::MoreFragment, item.tag, contentWrapper, fragmentContainer)
             true
         }
     }
@@ -114,7 +111,7 @@
 
 context(deckPicker: DeckPicker)
 private fun showBottomNavFragment(
-    fragment: Fragment,
+    newFragment: () -> Fragment,
     tag: String,
     contentWrapper: View,
     fragmentContainer: View,
@@ -128,15 +125,21 @@
         fragmentContainer.layoutParams = lp
     }
     deckPicker.supportFragmentManager.commit {
-        deckPicker.supportFragmentManager.fragments.forEach { frag ->
-            if (frag.id == R.id.bottom_nav_fragment_container) hide(frag)
-        }
+        hideBottomNavFragments()
         val existing = deckPicker.supportFragmentManager.findFragmentByTag(tag)
         if (existing != null) {
             show(existing)
         } else {
-            add(R.id.bottom_nav_fragment_container, fragment, tag)
+            add(R.id.bottom_nav_fragment_container, newFragment(), tag)
         }
     }
     fragmentContainer.isVisible = true
 }
+
+/** Hides any fragments currently hosted in the bottom-nav container. */
+context(deckPicker: DeckPicker)
+private fun FragmentTransaction.hideBottomNavFragments() {
+    deckPicker.supportFragmentManager.fragments.forEach { fragment ->
+        if (fragment.id == R.id.bottom_nav_fragment_container) hide(fragment)
+    }
+}
Index: AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt b/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt
--- a/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt	(revision a4f62edda816b0217f907d1232c64d36a666d8f4)
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/BottomNavController.kt	(date 1782502216202)
@@ -49,11 +49,13 @@
 
     enum class NavigationItem(
         @IdRes val id: Int,
+        /** Fragment tag used to find and reuse this destination's fragment across tab switches. */
+        val tag: String,
     ) {
-        HOME(R.id.nav_home),
-        BROWSER(R.id.nav_browser),
-        STATS(R.id.nav_stats),
-        MORE(R.id.nav_more),
+        HOME(R.id.nav_home, "home"),
+        BROWSER(R.id.nav_browser, "browser"),
+        STATS(R.id.nav_stats, "stats"),
+        MORE(R.id.nav_more, "more"),
         ;
 
         companion object {

@sanjaysargam

Copy link
Copy Markdown
Member

Assisted by Opus 4.6 for tests, used for general research otherwise.

you may need to remove tests from PR description

android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:background="?attr/colorSurface"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SearchBar child likely sets its own surface color. Worth confirming there's no double background flash on low-end devices, but this is likely fine

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen any flashes on a pixel 6 emulator, any specific model that may encounter this?

Comment thread AnkiDroid/src/main/java/com/ichi2/anki/pages/Statistics.kt Outdated
@ShaanNarendran

Copy link
Copy Markdown
Contributor Author

Done!

@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch from 6d2df48 to 47bd97d Compare June 27, 2026 08:41
@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch from 47bd97d to 22dfec6 Compare June 27, 2026 11:40
Forces the Card Browser to use the self-contained SearchView layout
whenever it is hosted within the DeckPicker's bottom navigation, as
the legacy layout's MenuProvider bleeds into the host activity's toolbar.
Integrates the bottom navigation bar into the DeckPicker using
the `HomeScreenNavigation` controller when the developer option is
enabled. Uses a show/hide fragment strategy for preserving tab state
and hides the back button in child fragments as appropriate.
Added `fitsSystemWindows=true` alongside `appBarColor` and `colorSurface`
to the app bars of the More, Card Browser SearchView, and Statistics
destinations to ensure they render edge-to-edge correctly underneath
the transparent status bar without overlapping system icons.
@ShaanNarendran ShaanNarendran force-pushed the feat/wire-deck-picker-bottom-nav branch from 22dfec6 to f487648 Compare June 27, 2026 20:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

GSoC Pull requests authored by a Google Summer of Code participant [Candidate/Selected], for GSoC mentors Needs Review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants