Home | Documentation Navigation
The tab system enables opening multiple Markdown and PDF documents simultaneously with efficient navigation between them. The implementation follows modern Android best practices and a Clean Architecture approach.
-
Multi-tab Support
- Up to 10 concurrently open documents
- Automatic management when the tab limit is reached (oldest tabs are closed)
- Persistence across app restarts
-
Horizontal Swipe Gestures
- Swipe left/right to switch between tabs
- Smooth animations using
HorizontalPager - Visual feedback during swipe
-
Tab Bar UI
- Scrollable tab bar at the top of the screen
- File icons (PDF/MD) for quick recognition
- Close button on each tab
- Long-press to close
- Active tab is highlighted
-
Navigation History
- Up to 20 recently viewed documents
- Quick access via QuickTabSwitcher
- "Recently used" section
-
Persistence
- Automatically save open tabs
- Restore tabs after app restart
- Save per-tab page positions
Path: app/src/main/java/com/example/checklist_interactive/data/tabs/TabManager.kt
Responsibilities:
- State management for tabs (StateFlow)
- Persistence via SharedPreferences
- Navigation history management
- Tab lifecycle (open, close, switch)
Important Methods:
fun openTab(fileInfo: FileInfo, pageNumber: Int = -1)
fun closeTab(index: Int)
fun switchToTab(index: Int)
fun getActiveTab(): TabInfo?
fun updateCurrentTabPage(pageNumber: Int)
fun navigateToPreviousInHistory(): TabInfo?State Flows:
val openTabs: StateFlow<List<TabInfo>>
val activeTabIndex: StateFlow<Int>
val navigationHistory: StateFlow<List<String>>Path: app/src/main/java/com/example/checklist_interactive/ui/tabs/TabBar.kt
Components:
TabBar- Horizontal tab barTabItem- Single tabTabbedDocumentViewer- Combination of TabBar + HorizontalPagerCompactTabIndicator- Compact tab indicator
Features:
- Material Design 3 Styling
- Responsive Layout
- Touch-Feedback
- Accessibility-Support
Path: app/src/main/java/com/example/checklist_interactive/ui/tabs/QuickTabSwitcher.kt
Components:
QuickTabSwitcherSheet- Bottom Sheet for the tab overviewQuickTabSwitchFAB- FAB to open the switcher
Features:
- "Recently used" section
- Liste aller offenen Tabs
- Active tab highlighted
// TabManager initialisieren
val tabManager = remember { TabManager(this@MainActivity) }
val openTabs by tabManager.openTabs.collectAsState()
val activeTabIndex by tabManager.activeTabIndex.collectAsState()LaunchedEffect(Unit) {
// After file import: restore tabs
val allFiles = fileManager.getAllFilesGrouped().values.flatten()
tabManager.restoreTabsFromPaths { path ->
allFiles.find { it.path == path }
}
tabManager.loadHistoryFromPreferences()
}// Open a file (create new tab or activate existing tab)
tabManager.openTab(fileInfo, pageNumber)
showFileList = false// Close a single tab
tabManager.closeTab(index)
// Close all tabs
tabManager.closeAllTabs()-
Open a Tab:
- Select a file from the list
- The file opens in a new tab
- Existing tabs remain open
-
Switching between tabs:
- Swipe: Swipe left/right across the document
- Tab bar: Tap a tab in the top bar
- Quick Switcher: Press the FAB → choose a tab
-
Closing a tab:
- Click the X button on a tab
- Long-press on a tab
- Back button (closes the active tab)
-
Quick switching:
- Open Quick Tab Switcher
- "Recently used" shows frequently used documents
- Tap to switch
// Example: tab groups
class TabManager {
fun createTabGroup(tabs: List<TabInfo>, name: String) {
// Implementation
}
}// Example: duplicate tab
fun duplicateTab(index: Int) {
val tab = openTabs.value.getOrNull(index) ?: return
openTab(tab.fileInfo, tab.pageNumber)
}tab_paths- Pipe-separated list of file pathstab_pages- Pipe-separated list of page numbersactive_tab- Index of the active tabtab_history- Pipe-separated navigation history
tab_paths: "asset://checklists/A320.md|/storage/manual.pdf"
tab_pages: "5|-1"
active_tab: 1
tab_history: "/storage/manual.pdf|asset://checklists/A320.md"
-
Lazy Loading:
- HorizontalPager renders only visible + adjacent pages
- Documents are loaded on-demand
-
State management:
- StateFlow for reactive updates
- remember() for UI state
-
Memory Management:
- Tab limit (10) prevents memory issues
- Old tabs are automatically closed
-
Consistency:
- Material Design 3 guidelines
- Consistent gestures across the app
-
Feedback:
- Visual confirmation when switching tabs
- Animations to improve clarity
-
Accessibility:
- Content descriptions for icons
- Touch targets at least 48dp
-
Tab groups:
- Group related documents
- Color coding
-
Tab pinning:
- Pin important tabs
- Prevent from being closed automatically
-
Tab search:
- Search across all open tabs
- Filter by type (MD/PDF)
-
Keyboard-Shortcuts:
- Ctrl+Tab for tab switch
- Ctrl+W to close
data class TabInfo(
val fileInfo: FileInfo,
val pageNumber: Int = -1,
val isPinned: Boolean = false // New
)
fun closeTab(index: Int) {
val tab = _openTabs.value.getOrNull(index) ?: return
if (tab.isPinned) return // Do not close when pinned
// ... rest of implementation
}data class TabGroup(
val name: String,
val tabs: List<TabInfo>,
val color: Color
)
private val _tabGroups = MutableStateFlow<List<TabGroup>>(emptyList())
val tabGroups: StateFlow<List<TabGroup>> = _tabGroups.asStateFlow()Solution:
- Ensure
restoreTabsFromPaths()is called after FileManager initialization - Check logs:
TabManageremits debug information
Solution:
- Ensure
TabbedDocumentVieweris used - Verify there are no conflicting gesture detectors in the content
Solution:
- Combine
TabBarwithTabbedDocumentViewerin a Column() - Check if
tabs.isEmpty()— the bar is hidden when zero tabs
@Test
fun testTabLimit() {
val tabManager = TabManager(context)
repeat(15) { i ->
tabManager.openTab(createMockFileInfo("file$i"))
}
assertEquals(10, tabManager.openTabs.value.size)
}
@Test
fun testNavigationHistory() {
val tabManager = TabManager(context)
val file1 = createMockFileInfo("file1")
val file2 = createMockFileInfo("file2")
tabManager.openTab(file1)
tabManager.openTab(file2)
val previousTab = tabManager.navigateToPreviousInHistory()
assertEquals(file1.path, previousTab?.fileInfo?.path)
}@Test
fun testTabSwitchBySwipe() {
// Open 2 tabs
onView(withId(R.id.file_list))
.perform(click())
// Swipe left
onView(withId(R.id.pager))
.perform(swipeLeft())
// Check if tab 2 is active
onView(withText("Tab 2"))
.check(matches(isDisplayed()))
}MainActivity.kt- IntegrationTabManager.kt- DatenlogikTabBar.kt- Tab-UIQuickTabSwitcher.kt- Quick-AccessInternalFileViewer.kt- Document viewer
// build.gradle.kts
implementation("androidx.compose.foundation:foundation:1.5.4")
implementation("androidx.compose.material3:material3:1.1.2")- CHECKLIST_FEATURE.md - Checklist-System
- TAG_SYSTEM.md - Tag-System
- QUICKNOTES_ARCHITECTURE.md - QuickNotes
- ✅ Initial tab system implementation
- ✅ TabManager with persistence
- ✅ TabBar UI-Komponente
- ✅ HorizontalPager for swipe gestures
- ✅ QuickTabSwitcher for quick access
- ✅ Navigation-History
- ✅ MainActivity-Integration
Same license as the main project.