diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt
index 3da9f162c..c1515fa7e 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt
@@ -109,7 +109,7 @@ class TopLevelDestinationTest : BaseActivityTest() {
fun testTopLevelDestination() {
topLevel {
clickReaderOnBottomNav {
- assertReaderScreenDisplayed()
+ assertReaderScreenDisplayed(composeTestRule)
}
clickDownloadOnBottomNav {
onlineLibrary {
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/library/LibraryRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/library/LibraryRobot.kt
index 5c830c374..44c464c1c 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/library/LibraryRobot.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/library/LibraryRobot.kt
@@ -42,6 +42,7 @@ import org.kiwix.kiwixmobile.nav.destination.library.local.NO_FILE_TEXT_TESTING_
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.testutils.TestUtils.refresh
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
+import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
import org.kiwix.kiwixmobile.ui.BOOK_ITEM_TESTING_TAG
fun library(func: LibraryRobot.() -> Unit) = LibraryRobot().applyWithViewHierarchyPrinting(func)
@@ -102,8 +103,8 @@ class LibraryRobot : BaseRobot() {
}
fun waitUntilZimFilesRefreshing(composeTestRule: ComposeContentTestRule) {
- pauseForBetterTestPerformance()
testFlakyView({
+ composeTestRule.waitUntilTimeout()
composeTestRule.onNodeWithTag(CONTENT_LOADING_PROGRESSBAR_TESTING_TAG)
.assertIsNotDisplayed()
})
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/reader/ReaderRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/reader/ReaderRobot.kt
index a74c3705d..70989eae2 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/reader/ReaderRobot.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/nav/destination/reader/ReaderRobot.kt
@@ -18,15 +18,20 @@
package org.kiwix.kiwixmobile.nav.destination.reader
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.onNodeWithTag
import applyWithViewHierarchyPrinting
import org.kiwix.kiwixmobile.BaseRobot
-import org.kiwix.kiwixmobile.Findable.ViewId
-import org.kiwix.kiwixmobile.core.R
+import org.kiwix.kiwixmobile.core.main.reader.READER_SCREEN_TESTING_TAG
+import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
fun reader(func: ReaderRobot.() -> Unit) = ReaderRobot().applyWithViewHierarchyPrinting(func)
class ReaderRobot : BaseRobot() {
- fun assertReaderScreenDisplayed() {
- isVisible(ViewId(R.id.activity_main_root))
+ fun assertReaderScreenDisplayed(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitUntilTimeout()
+ onNodeWithTag(READER_SCREEN_TESTING_TAG).assertExists()
+ }
}
}
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryRobot.kt
index 1c234ba80..41eeb1e4b 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryRobot.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryRobot.kt
@@ -20,11 +20,13 @@ package org.kiwix.kiwixmobile.page.history
import android.util.Log
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.longClick
+import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTouchInput
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
-import androidx.test.espresso.action.ViewActions.longClick
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.web.sugar.Web.onWebView
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
@@ -36,11 +38,13 @@ import junit.framework.AssertionFailedError
import org.kiwix.kiwixmobile.BaseRobot
import org.kiwix.kiwixmobile.Findable.ViewId
import org.kiwix.kiwixmobile.core.R
+import org.kiwix.kiwixmobile.core.main.reader.TAB_SWITCHER_VIEW_TESTING_TAG
import org.kiwix.kiwixmobile.core.page.DELETE_MENU_ICON_TESTING_TAG
import org.kiwix.kiwixmobile.core.ui.components.TOOLBAR_TITLE_TESTING_TAG
import org.kiwix.kiwixmobile.core.utils.dialog.ALERT_DIALOG_TITLE_TEXT_TESTING_TAG
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
+import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
fun navigationHistory(func: NavigationHistoryRobot.() -> Unit) =
NavigationHistoryRobot().applyWithViewHierarchyPrinting(func)
@@ -55,12 +59,14 @@ class NavigationHistoryRobot : BaseRobot() {
isVisible(ViewId(readerFragment))
}
- fun closeTabSwitcherIfVisible() {
+ fun closeTabSwitcherIfVisible(composeTestRule: ComposeContentTestRule) {
try {
- pauseForBetterTestPerformance()
- isVisible(ViewId(R.id.tab_switcher_close_all_tabs))
- pressBack()
- } catch (_: Exception) {
+ composeTestRule.apply {
+ waitUntilTimeout()
+ onNodeWithTag(TAB_SWITCHER_VIEW_TESTING_TAG).assertExists()
+ pressBack()
+ }
+ } catch (_: AssertionError) {
Log.i(
"NAVIGATION_HISTORY_TEST",
"Couldn't found tab switcher, probably it is not visible"
@@ -91,14 +97,24 @@ class NavigationHistoryRobot : BaseRobot() {
)
}
- fun longClickOnBackwardButton() {
- pauseForBetterTestPerformance()
- testFlakyView({ onView(withId(R.id.bottom_toolbar_arrow_back)).perform(longClick()) })
+ fun longClickOnBackwardButton(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitUntilTimeout()
+ testFlakyView({
+ onNodeWithContentDescription(context.getString(R.string.go_to_previous_page))
+ .performTouchInput { longClick() }
+ })
+ }
}
- fun longClickOnForwardButton() {
- pauseForBetterTestPerformance()
- longClickOn(ViewId(R.id.bottom_toolbar_arrow_forward))
+ fun longClickOnForwardButton(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitUntilTimeout()
+ testFlakyView({
+ onNodeWithContentDescription(context.getString(R.string.go_to_next_page))
+ .performTouchInput { longClick() }
+ })
+ }
}
fun assertBackwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
@@ -117,9 +133,12 @@ class NavigationHistoryRobot : BaseRobot() {
}
}
- fun clickOnBackwardButton() {
- pauseForBetterTestPerformance()
- clickOn(ViewId(R.id.bottom_toolbar_arrow_back))
+ fun clickOnBackwardButton(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitUntilTimeout()
+ onNodeWithContentDescription(context.getString(R.string.go_to_previous_page))
+ .performClick()
+ }
}
fun assertForwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryTest.kt
index da96ca91b..72fdd987d 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/page/history/NavigationHistoryTest.kt
@@ -147,14 +147,14 @@ class NavigationHistoryTest : BaseActivityTest() {
}
StandardActions.closeDrawer() // close the drawer if open before running the test cases.
navigationHistory {
- closeTabSwitcherIfVisible()
+ closeTabSwitcherIfVisible(composeTestRule)
checkZimFileLoadedSuccessful(R.id.readerFragment)
clickOnAndroidArticle()
- longClickOnBackwardButton()
+ longClickOnBackwardButton(composeTestRule)
assertBackwardNavigationHistoryDialogDisplayed(composeTestRule)
pressBack()
- clickOnBackwardButton()
- longClickOnForwardButton()
+ clickOnBackwardButton(composeTestRule)
+ longClickOnForwardButton(composeTestRule)
assertForwardNavigationHistoryDialogDisplayed(composeTestRule)
clickOnDeleteHistory(composeTestRule)
assertDeleteDialogDisplayed(composeTestRule)
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/KiwixReaderFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/KiwixReaderFragmentTest.kt
index bfd6bbd3c..3ac58bb91 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/KiwixReaderFragmentTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/KiwixReaderFragmentTest.kt
@@ -19,6 +19,7 @@
package org.kiwix.kiwixmobile.reader
import android.os.Build
+import androidx.compose.ui.test.junit4.createComposeRule
import androidx.core.content.edit
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
@@ -46,6 +47,7 @@ import org.kiwix.kiwixmobile.BaseActivityTest
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
+import org.kiwix.kiwixmobile.core.utils.TestingUtils.COMPOSE_TEST_RULE_ORDER
import org.kiwix.kiwixmobile.core.utils.TestingUtils.RETRY_RULE_ORDER
import org.kiwix.kiwixmobile.main.KiwixMainActivity
import org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragmentDirections
@@ -64,6 +66,9 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
@JvmField
val retryRule = RetryRule()
+ @get:Rule(order = COMPOSE_TEST_RULE_ORDER)
+ val composeTestRule = createComposeRule()
+
private lateinit var kiwixMainActivity: KiwixMainActivity
@Before
@@ -136,10 +141,10 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
openKiwixReaderFragmentWithFile(zimFile)
reader {
checkZimFileLoadedSuccessful(R.id.readerFragment)
- clickOnTabIcon()
- clickOnClosedAllTabsButton()
- clickOnUndoButton()
- assertTabRestored()
+ clickOnTabIcon(composeTestRule)
+ clickOnClosedAllTabsButton(composeTestRule)
+ clickOnUndoButton(composeTestRule)
+ assertTabRestored(composeTestRule)
pressBack()
checkZimFileLoadedSuccessful(R.id.readerFragment)
}
@@ -171,6 +176,8 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
openKiwixReaderFragmentWithFile(downloadingZimFile)
reader {
checkZimFileLoadedSuccessful(R.id.readerFragment)
+ clickOnTabIcon(composeTestRule)
+ clickOnTabIcon(composeTestRule)
// test the whole welcome page is loaded or not
assertArticleLoaded("Hydrogène")
assertArticleLoaded("Automobile")
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/ReaderRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/ReaderRobot.kt
index a85a4e7be..feb384d6e 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/ReaderRobot.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/reader/ReaderRobot.kt
@@ -18,10 +18,12 @@
package org.kiwix.kiwixmobile.reader
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.ViewActions.click
-import androidx.test.espresso.matcher.ViewMatchers.withId
-import androidx.test.espresso.matcher.ViewMatchers.withText
+import androidx.compose.ui.test.assertTextEquals
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.onAllNodesWithTag
+import androidx.compose.ui.test.onNodeWithTag
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
import androidx.test.espresso.web.sugar.Web.onWebView
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
@@ -29,11 +31,13 @@ import androidx.test.espresso.web.webdriver.Locator
import applyWithViewHierarchyPrinting
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
import org.kiwix.kiwixmobile.BaseRobot
-import org.kiwix.kiwixmobile.Findable.Text
import org.kiwix.kiwixmobile.Findable.ViewId
-import org.kiwix.kiwixmobile.core.R
+import org.kiwix.kiwixmobile.core.main.reader.CLOSE_ALL_TABS_BUTTON_TESTING_TAG
+import org.kiwix.kiwixmobile.core.main.reader.TAB_MENU_ITEM_TESTING_TAG
+import org.kiwix.kiwixmobile.core.main.reader.TAB_TITLE_TESTING_TAG
import org.kiwix.kiwixmobile.testutils.TestUtils
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
+import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
fun reader(func: ReaderRobot.() -> Unit) = ReaderRobot().applyWithViewHierarchyPrinting(func)
@@ -45,30 +49,43 @@ class ReaderRobot : BaseRobot() {
isVisible(ViewId(readerFragment))
}
- fun clickOnTabIcon() {
- pauseForBetterTestPerformance()
- testFlakyView({ onView(withId(R.id.ic_tab_switcher_text)).perform(click()) })
+ fun clickOnTabIcon(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitUntilTimeout()
+ testFlakyView({
+ onNodeWithTag(TAB_MENU_ITEM_TESTING_TAG).performClick()
+ })
+ }
}
- fun clickOnClosedAllTabsButton() {
- pauseForBetterTestPerformance()
- clickOn(ViewId(R.id.tab_switcher_close_all_tabs))
+ fun clickOnClosedAllTabsButton(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitUntilTimeout()
+ testFlakyView({
+ onNodeWithTag(CLOSE_ALL_TABS_BUTTON_TESTING_TAG).performClick()
+ })
+ }
}
- fun clickOnUndoButton() {
+ fun clickOnUndoButton(composeTestRule: ComposeContentTestRule) {
try {
- onView(withText("UNDO")).perform(click())
- } catch (runtimeException: RuntimeException) {
+ composeTestRule.apply {
+ onNodeWithText("UNDO", useUnmergedTree = true)
+ .performClick()
+ }
+ } catch (_: AssertionError) {
if (retryCountForClickOnUndoButton > 0) {
retryCountForClickOnUndoButton--
- clickOnUndoButton()
+ clickOnUndoButton(composeTestRule)
}
}
}
- fun assertTabRestored() {
- pauseForBetterTestPerformance()
- isVisible(Text("Test Zim"))
+ fun assertTabRestored(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitUntilTimeout()
+ onAllNodesWithTag(TAB_TITLE_TESTING_TAG)[0].assertTextEquals("Test Zim")
+ }
}
private fun pauseForBetterTestPerformance() {
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/search/SearchRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/search/SearchRobot.kt
index ba2509ed8..f89947460 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/search/SearchRobot.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/search/SearchRobot.kt
@@ -27,9 +27,6 @@ import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextClearance
import androidx.compose.ui.test.performTextInput
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.ViewActions.click
-import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.web.sugar.Web.onWebView
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
import androidx.test.espresso.web.webdriver.Locator
@@ -38,7 +35,7 @@ import applyWithViewHierarchyPrinting
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
import org.kiwix.kiwixmobile.BaseRobot
import org.kiwix.kiwixmobile.Findable.ViewId
-import org.kiwix.kiwixmobile.core.R
+import org.kiwix.kiwixmobile.core.page.SEARCH_ICON_TESTING_TAG
import org.kiwix.kiwixmobile.core.search.SEARCH_FIELD_TESTING_TAG
import org.kiwix.kiwixmobile.core.search.SEARCH_ITEM_TESTING_TAG
import org.kiwix.kiwixmobile.core.ui.components.NAVIGATION_ICON_TESTING_TAG
@@ -122,12 +119,16 @@ class SearchRobot : BaseRobot() {
composeTestRule.onNodeWithTag(NAVIGATION_ICON_TESTING_TAG).performClick()
}
- private fun openSearchScreen() {
- testFlakyView({ onView(withId(R.id.menu_search)).perform(click()) })
+ private fun openSearchScreen(composeTestRule: ComposeContentTestRule) {
+ testFlakyView(
+ {
+ composeTestRule.onNodeWithTag(SEARCH_ICON_TESTING_TAG).performClick()
+ }
+ )
}
fun searchAndClickOnArticle(searchString: String, composeTestRule: ComposeContentTestRule) {
- openSearchScreen()
+ openSearchScreen(composeTestRule)
searchWithFrequentlyTypedWords(searchString, composeTestRule = composeTestRule)
clickOnSearchItemInSearchList(composeTestRule)
checkZimFileSearchSuccessful(org.kiwix.kiwixmobile.R.id.readerFragment)
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/shortcuts/GetContentShortcutTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/shortcuts/GetContentShortcutTest.kt
index 1ddd8b795..3085a21e8 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/shortcuts/GetContentShortcutTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/shortcuts/GetContentShortcutTest.kt
@@ -128,7 +128,7 @@ class GetContentShortcutTest {
onlineLibrary { assertOnlineLibraryFragmentDisplayed(composeTestRule) }
topLevel {
clickReaderOnBottomNav {
- assertReaderScreenDisplayed()
+ assertReaderScreenDisplayed(composeTestRule)
}
clickDownloadOnBottomNav {
onlineLibrary { assertOnlineLibraryFragmentDisplayed(composeTestRule) }
diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt
index 8b65c91fe..abfe10b1b 100644
--- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt
+++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/reader/KiwixReaderFragment.kt
@@ -48,6 +48,7 @@ import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.extensions.update
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
+import org.kiwix.kiwixmobile.core.main.reader.HIDE_TAB_SWITCHER_DELAY
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromExternalLaunch
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromSearchScreen
@@ -62,8 +63,6 @@ import org.kiwix.kiwixmobile.core.utils.files.FileUtils
import org.kiwix.kiwixmobile.core.utils.files.Log
import java.io.File
-private const val HIDE_TAB_SWITCHER_DELAY: Long = 300
-
class KiwixReaderFragment : CoreReaderFragment() {
private var isFullScreenVideo: Boolean = false
diff --git a/app/src/main/res/menu/menu_language.xml b/app/src/main/res/menu/menu_language.xml
deleted file mode 100644
index 980a1e234..000000000
--- a/app/src/main/res/menu/menu_language.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
diff --git a/app/src/main/res/menu/menu_zim_manager.xml b/app/src/main/res/menu/menu_zim_manager.xml
deleted file mode 100644
index 9615f69b5..000000000
--- a/app/src/main/res/menu/menu_zim_manager.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-
-
-
diff --git a/app/src/main/res/menu/wifi_file_share_items.xml b/app/src/main/res/menu/wifi_file_share_items.xml
deleted file mode 100644
index 671bcf2e9..000000000
--- a/app/src/main/res/menu/wifi_file_share_items.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/ActivityModule.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/ActivityModule.kt
index a5d38dcf4..d769da320 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/ActivityModule.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/ActivityModule.kt
@@ -17,19 +17,12 @@
*/
package org.kiwix.kiwixmobile.core.di.modules
-import android.app.Activity
-import android.view.Menu
import dagger.Binds
import dagger.Module
import dagger.Provides
import org.kiwix.kiwixmobile.core.data.DataSource
import org.kiwix.kiwixmobile.core.di.ActivityScope
-import org.kiwix.kiwixmobile.core.main.KiwixWebView
-import org.kiwix.kiwixmobile.core.main.MainMenu
-import org.kiwix.kiwixmobile.core.main.MainMenu.Factory
-import org.kiwix.kiwixmobile.core.main.MainMenu.MenuClickListener
import org.kiwix.kiwixmobile.core.main.MainRepositoryActions
-import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
@@ -44,33 +37,5 @@ abstract class ActivityModule {
@ActivityScope
fun providesMainPresenter(dataSource: DataSource): MainRepositoryActions =
MainRepositoryActions(dataSource)
-
- @Provides
- @ActivityScope
- fun providesMainMenuFactory(
- activity: Activity,
- zimReaderContainer: ZimReaderContainer
- ): MainMenu.Factory = object : Factory {
- override fun create(
- menu: Menu,
- webViews: MutableList,
- urlIsValid: Boolean,
- menuClickListener: MenuClickListener,
- disableReadAloud: Boolean,
- disableTabs: Boolean,
- disableSearch: Boolean
- ): MainMenu =
- MainMenu(
- activity,
- zimReaderContainer.zimFileReader,
- menu,
- webViews,
- urlIsValid,
- disableReadAloud,
- disableTabs,
- disableSearch,
- menuClickListener
- )
- }
}
}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainMenu.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainMenu.kt
deleted file mode 100644
index 415dcaa9c..000000000
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/MainMenu.kt
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * Kiwix Android
- * Copyright (c) 2019 Kiwix
- * 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 org.kiwix.kiwixmobile.core.main
-
-import android.app.Activity
-import android.content.res.Configuration
-import android.view.Menu
-import android.view.MenuItem
-import android.widget.TextView
-import androidx.core.view.isVisible
-import org.kiwix.kiwixmobile.core.R
-import org.kiwix.kiwixmobile.core.extensions.setToolTipWithContentDescription
-import org.kiwix.kiwixmobile.core.reader.ZimFileReader
-
-const val REQUEST_FILE_SEARCH = 1236
-
-@Suppress("LongParameterList")
-class MainMenu(
- private val activity: Activity,
- zimFileReader: ZimFileReader?,
- menu: Menu,
- webViews: MutableList,
- urlIsValid: Boolean,
- disableReadAloud: Boolean = false,
- disableTabs: Boolean = false,
- private val disableSearch: Boolean = false,
- private val menuClickListener: MenuClickListener
-) {
- interface Factory {
- fun create(
- menu: Menu,
- webViews: MutableList,
- urlIsValid: Boolean,
- menuClickListener: MenuClickListener,
- disableReadAloud: Boolean,
- disableTabs: Boolean,
- disableSearch: Boolean = false
- ): MainMenu
- }
-
- interface MenuClickListener {
- fun onTabMenuClicked()
- fun onHomeMenuClicked()
- fun onAddNoteMenuClicked()
- fun onRandomArticleMenuClicked()
- fun onReadAloudMenuClicked()
- fun onFullscreenMenuClicked()
- fun onSearchMenuClickedMenuClicked()
- }
-
- init {
- activity.menuInflater.inflate(R.menu.menu_main, menu)
- }
-
- private val search = menu.findItem(R.id.menu_search)
- private var tabSwitcher: MenuItem? = menu.findItem(R.id.menu_tab_switcher)
- private var tabSwitcherTextView: TextView? =
- tabSwitcher?.actionView?.findViewById(R.id.ic_tab_switcher_text)
- private val addNote = menu.findItem(R.id.menu_add_note)
- private val randomArticle = menu.findItem(R.id.menu_random_article)
- private val fullscreen = menu.findItem(R.id.menu_fullscreen)
- private var readAloud: MenuItem? = menu.findItem(R.id.menu_read_aloud)
- private var isInTabSwitcher: Boolean = false
-
- init {
- if (disableReadAloud) {
- readAloud?.isVisible = false
- readAloud = null
- }
- if (disableTabs) {
- tabSwitcher?.isVisible = false
- tabSwitcherTextView?.isVisible = false
- tabSwitcher = null
- tabSwitcherTextView = null
- }
-
- if (disableSearch) {
- search?.isVisible = false
- }
-
- randomArticle.setShowAsAction(
- if (activity.resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
- MenuItem.SHOW_AS_ACTION_IF_ROOM
- } else {
- MenuItem.SHOW_AS_ACTION_NEVER
- }
- )
- tabSwitcher?.actionView?.apply {
- setOnClickListener { menuClickListener.onTabMenuClicked() }
- setToolTipWithContentDescription(resources.getString(R.string.switch_tabs))
- }
- addNote.menuItemClickListener { menuClickListener.onAddNoteMenuClicked() }
- randomArticle.menuItemClickListener { menuClickListener.onRandomArticleMenuClicked() }
- readAloud.menuItemClickListener { menuClickListener.onReadAloudMenuClicked() }
- fullscreen.menuItemClickListener { menuClickListener.onFullscreenMenuClicked() }
-
- showWebViewOptions(urlIsValid)
- zimFileReader?.let {
- onFileOpened(urlIsValid)
- }
- updateTabIcon(webViews.size)
- }
-
- fun onOptionsItemSelected(item: MenuItem) =
- when (item.itemId) {
- android.R.id.home -> {
- menuClickListener.onHomeMenuClicked()
- true
- }
-
- else -> false
- }
-
- fun onFileOpened(urlIsValid: Boolean) {
- setVisibility(urlIsValid, randomArticle, search, readAloud, addNote, fullscreen, tabSwitcher)
- search.setOnMenuItemClickListener { navigateToSearch() }
- }
-
- fun hideBookSpecificMenuItems() {
- setVisibility(false, search, tabSwitcher, randomArticle, addNote, readAloud)
- }
-
- fun showBookSpecificMenuItems() {
- setVisibility(true, search, tabSwitcher, randomArticle, addNote, readAloud)
- }
-
- fun showTabSwitcherOptions() {
- isInTabSwitcher = true
- setVisibility(false, randomArticle, readAloud, addNote, fullscreen)
- }
-
- fun showWebViewOptions(urlIsValid: Boolean) {
- isInTabSwitcher = false
- fullscreen.isVisible = true
- setVisibility(urlIsValid, randomArticle, search, readAloud, addNote, tabSwitcher)
- }
-
- fun updateTabIcon(tabs: Int) {
- tabSwitcherTextView?.text = if (tabs > 99) ":D" else "$tabs"
- }
-
- private fun navigateToSearch(): Boolean {
- menuClickListener.onSearchMenuClickedMenuClicked()
- return true
- }
-
- fun onTextToSpeechStartedTalking() {
- readAloud?.setTitle(R.string.menu_read_aloud_stop)
- }
-
- fun onTextToSpeechStoppedTalking() {
- readAloud?.setTitle(R.string.menu_read_aloud)
- }
-
- private fun setVisibility(visibility: Boolean, vararg menuItems: MenuItem?) {
- menuItems.forEach {
- if (it == search && disableSearch) {
- it?.isVisible = false
- } else {
- it?.isVisible = visibility
- }
- }
- }
-
- fun tryExpandSearch(zimFileReader: ZimFileReader?) {
- if (search.isVisible && zimFileReader != null) {
- navigateToSearch()
- }
- }
-
- fun isInTabSwitcher(): Boolean = isInTabSwitcher
-}
-
-private fun MenuItem?.menuItemClickListener(function: (MenuItem) -> Unit) {
- this?.setOnMenuItemClickListener {
- function.invoke(it)
- true
- }
-}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/TabsAdapter.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/TabsAdapter.kt
deleted file mode 100644
index 38f99f8e2..000000000
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/TabsAdapter.kt
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Kiwix Android
- * Copyright (c) 2019 Kiwix
- * 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 org.kiwix.kiwixmobile.core.main
-
-import android.annotation.SuppressLint
-import android.text.TextUtils
-import android.util.TypedValue
-import android.view.View
-import android.view.ViewGroup
-import android.widget.FrameLayout
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.appcompat.app.AppCompatActivity
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
-import androidx.constraintlayout.widget.ConstraintSet.END
-import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
-import androidx.constraintlayout.widget.ConstraintSet.START
-import androidx.constraintlayout.widget.ConstraintSet.TOP
-import androidx.recyclerview.widget.RecyclerView
-import com.google.android.material.card.MaterialCardView
-import org.kiwix.kiwixmobile.core.R
-import com.google.android.material.R.attr
-import org.kiwix.kiwixmobile.core.extensions.getAttribute
-import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
-import org.kiwix.kiwixmobile.core.extensions.setToolTipWithContentDescription
-import org.kiwix.kiwixmobile.core.extensions.tint
-import org.kiwix.kiwixmobile.core.utils.DimenUtils.getToolbarHeight
-import org.kiwix.kiwixmobile.core.utils.DimenUtils.getWindowHeight
-import org.kiwix.kiwixmobile.core.utils.DimenUtils.getWindowWidth
-import org.kiwix.kiwixmobile.core.utils.StyleUtils.fromHtml
-
-class TabsAdapter internal constructor(
- private val activity: AppCompatActivity,
- private val webViews: List,
- private val painter: DarkModeViewPainter
-) : RecyclerView.Adapter() {
- init {
- setHasStableIds(true)
- }
-
- private var listener: TabClickListener? = null
- var selected = 0
-
- @SuppressLint("ResourceType") override fun onCreateViewHolder(
- parent: ViewGroup,
- viewType: Int
- ): ViewHolder {
- val context = parent.context
- val margin16 = context.resources.getDimensionPixelSize(R.dimen.activity_horizontal_margin)
- val closeImageWidthAndHeight =
- context.resources.getDimensionPixelSize(R.dimen.close_tab_button_size)
- val close =
- ImageView(context)
- .apply {
- id = R.id.tabsAdapterCloseImageView
- setImageDrawableCompat(R.drawable.ic_clear_white_24dp)
- setToolTipWithContentDescription(resources.getString(R.string.close_tab))
- val outValue = TypedValue()
- context.theme.resolveAttribute(android.R.attr.actionBarItemBackground, outValue, true)
- setBackgroundResource(outValue.resourceId)
- tint(context.getAttribute(attr.colorOnSurface))
- }
- val cardView =
- MaterialCardView(context)
- .apply {
- id = R.id.tabsAdapterCardView
- useCompatPadding = true
- }
- val textView =
- TextView(context)
- .apply {
- id = R.id.tabsAdapterTextView
- maxLines = 1
- ellipsize = TextUtils.TruncateAt.END
- }
- val constraintLayout =
- ConstraintLayout(context)
- .apply {
- isFocusableInTouchMode = true
- addView(
- cardView,
- ConstraintLayout.LayoutParams(
- activity.getWindowWidth() / 2,
- -activity.getToolbarHeight() / 2 + activity.getWindowHeight() / 2
- )
- )
- addView(
- close,
- ConstraintLayout.LayoutParams(closeImageWidthAndHeight, closeImageWidthAndHeight)
- )
- layoutParams =
- RecyclerView.LayoutParams(
- RecyclerView.LayoutParams.WRAP_CONTENT,
- RecyclerView.LayoutParams.MATCH_PARENT
- )
- addView(
- textView,
- ConstraintLayout.LayoutParams(0, ConstraintLayout.LayoutParams.WRAP_CONTENT)
- )
- }
- ConstraintSet()
- .apply {
- clone(constraintLayout)
- connect(cardView.id, TOP, PARENT_ID, TOP)
- connect(cardView.id, BOTTOM, PARENT_ID, BOTTOM)
- connect(cardView.id, START, PARENT_ID, START, margin16)
- connect(cardView.id, END, PARENT_ID, END, margin16)
- connect(close.id, END, cardView.id, END)
- connect(close.id, BOTTOM, cardView.id, TOP)
- connect(textView.id, BOTTOM, cardView.id, TOP)
- connect(textView.id, START, cardView.id, START, margin16 / 8)
- connect(textView.id, END, close.id, START)
- applyTo(constraintLayout)
- }
- return ViewHolder(constraintLayout, textView, close, cardView)
- }
-
- override fun onBindViewHolder(holder: ViewHolder, position: Int) {
- val webView = webViews[position]
- webView.parent?.let { (it as ViewGroup).removeView(webView) }
- val webViewTitle = webView.title.fromHtml().toString()
- holder.apply {
- title.text = webViewTitle
- close.setOnClickListener { v: View -> listener?.onCloseTab(v, adapterPosition) }
- materialCardView.apply {
- removeAllViews()
- // Create a new FrameLayout to hold the web view and custom view
- val frameLayout = FrameLayout(context)
- // Add the web view to the frame layout
- frameLayout.addView(webView)
- // Create a custom view that covers the entire
- // webView(which prevent to clicks inside the webView) and handles tab selection
- val view =
- View(context).apply {
- layoutParams =
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT
- )
- setOnClickListener { v: View ->
- selected = adapterPosition
- listener?.onSelectTab(v, selected)
- notifyDataSetChanged()
- }
- }
- // Add the custom view to the frame layout
- frameLayout.addView(view)
- // Add the frame layout to the material card view
- addView(
- frameLayout,
- FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.MATCH_PARENT
- )
- )
- }
- }
- if (webViewTitle != activity.getString(R.string.menu_home)) {
- painter.update(webView) // if the webView is not opened yet
- }
- }
-
- override fun getItemCount(): Int = webViews.size
-
- override fun getItemId(position: Int): Long = webViews[position].hashCode().toLong()
-
- fun setTabClickListener(listener: TabClickListener) {
- this.listener = listener
- }
-
- interface TabClickListener {
- fun onSelectTab(view: View, position: Int)
- fun onCloseTab(view: View, position: Int)
- }
-
- class ViewHolder(
- view: View,
- val title: TextView,
- val close: ImageView,
- val materialCardView: MaterialCardView
- ) :
- RecyclerView.ViewHolder(view)
-}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt
index 8de176eb1..e4e2fc251 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/CoreReaderFragment.kt
@@ -27,7 +27,6 @@ import android.content.ServiceConnection
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Configuration
-import android.graphics.Canvas
import android.media.AudioManager
import android.media.AudioManager.OnAudioFocusChangeListener
import android.net.Uri
@@ -44,24 +43,18 @@ import android.view.Gravity.BOTTOM
import android.view.Gravity.CENTER_HORIZONTAL
import android.view.LayoutInflater
import android.view.Menu
-import android.view.MotionEvent
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
-import android.view.animation.AnimationUtils
import android.webkit.WebBackForwardList
import android.webkit.WebView
import android.widget.FrameLayout
-import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
-import androidx.annotation.AnimRes
-import androidx.appcompat.app.ActionBar
import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.Toolbar
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.SnackbarDuration
@@ -83,10 +76,8 @@ import androidx.core.view.isVisible
import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
-import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
import com.google.android.material.bottomappbar.BottomAppBar
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationView
@@ -114,14 +105,12 @@ import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
-import org.kiwix.kiwixmobile.core.databinding.FragmentReaderBinding
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.consumeObservable
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isLandScapeMode
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.observeNavigationResult
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.requestNotificationPermission
-import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.findFirstTextView
import org.kiwix.kiwixmobile.core.extensions.closeFullScreenMode
import org.kiwix.kiwixmobile.core.extensions.showFullScreenMode
import org.kiwix.kiwixmobile.core.extensions.snack
@@ -140,14 +129,11 @@ import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech
import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech.OnInitSucceedListener
import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech.OnSpeakingListener
import org.kiwix.kiwixmobile.core.main.KiwixWebView
-import org.kiwix.kiwixmobile.core.main.MainMenu
import org.kiwix.kiwixmobile.core.main.MainRepositoryActions
-import org.kiwix.kiwixmobile.core.main.OnSwipeTouchListener
import org.kiwix.kiwixmobile.core.main.ServiceWorkerUninitialiser
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.DocumentSection
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.TableClickListener
-import org.kiwix.kiwixmobile.core.main.TabsAdapter
import org.kiwix.kiwixmobile.core.main.UNINITIALISER_ADDRESS
import org.kiwix.kiwixmobile.core.main.WebViewCallback
import org.kiwix.kiwixmobile.core.main.WebViewProvider
@@ -202,10 +188,10 @@ import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Date
import javax.inject.Inject
-import kotlin.math.abs
import kotlin.math.max
const val SEARCH_ITEM_TITLE_KEY = "searchItemTitle"
+const val HIDE_TAB_SWITCHER_DELAY: Long = 300
@Suppress("LargeClass")
abstract class CoreReaderFragment :
@@ -219,15 +205,9 @@ abstract class CoreReaderFragment :
ShowDonationDialogCallback {
protected val webViewList = mutableStateListOf()
private val webUrlsFlow = MutableStateFlow("")
- private var fragmentReaderBinding: FragmentReaderBinding? = null
-
- var toolbar: Toolbar? = null
var drawerLayout: DrawerLayout? = null
protected var tableDrawerRightContainer: NavigationView? = null
- var tabSwitcherRoot: View? = null
-
- var activityMainRoot: View? = null
@JvmField
@Inject
@@ -262,10 +242,6 @@ abstract class CoreReaderFragment :
var painter: DarkModeViewPainter? = null
protected var currentWebViewIndex by mutableStateOf(0)
private var currentTtsWebViewIndex = 0
- protected var actionBar: ActionBar? = null
- protected var mainMenu: MainMenu? = null
-
- private var tabRecyclerView: RecyclerView? = null
private var isFirstTimeMainPageLoaded = true
private var isFromManageExternalLaunch = false
private val savingTabsMutex = Mutex()
@@ -295,7 +271,6 @@ abstract class CoreReaderFragment :
private var documentParser: DocumentParser? = null
private var tts: KiwixTextToSpeech? = null
private var compatCallback: CompatFindActionModeCallback? = null
- private var tabsAdapter: TabsAdapter? = null
private var zimReaderSource: ZimReaderSource? = null
private var actionMode: ActionMode? = null
private var tempWebViewForUndo: KiwixWebView? = null
@@ -304,7 +279,6 @@ abstract class CoreReaderFragment :
private var isFirstRun = false
private var tableDrawerAdapter: TableDrawerAdapter? = null
private var tableDrawerRight: RecyclerView? = null
- private var tabCallback: ItemTouchHelper.Callback? = null
private var donationLayout: FrameLayout? = null
private var bookmarkingJob: Job? = null
private var isBookmarked = false
@@ -517,53 +491,13 @@ abstract class CoreReaderFragment :
activity?.let {
WebView(it).destroy() // Workaround for buggy webViews see #710
}
- prepareViews()
handleLocaleCheck()
- activity?.setSupportActionBar(toolbar)
- actionBar = activity?.supportActionBar
initHideBackToTopTimer()
- initTabCallback()
- toolbar?.setOnTouchListener(
- object : OnSwipeTouchListener(requireActivity()) {
- @SuppressLint("SyntheticAccessor")
- override fun onSwipeBottom() {
- showTabSwitcher()
- }
-
- override fun onSwipeLeft() {
- if (currentWebViewIndex < webViewList.size - 1) {
- val current: View? = getCurrentWebView()
- startAnimation(current, R.anim.transition_left)
- selectTab(currentWebViewIndex + 1)
- }
- }
-
- override fun onSwipeRight() {
- if (currentWebViewIndex > 0) {
- val current: View? = getCurrentWebView()
- startAnimation(current, R.anim.transition_right)
- selectTab(currentWebViewIndex - 1)
- }
- }
-
- override fun onTap(e: MotionEvent?) {
- e?.let {
- val titleTextView = toolbar?.findFirstTextView() ?: return@onTap
- titleTextView.let {
- // only initiate search if it is on the reader screen
- mainMenu?.tryExpandSearch(zimReaderContainer?.zimFileReader)
- }
- }
- }
- }
- )
loadDrawerViews()
tableDrawerRight =
tableDrawerRightContainer?.getHeaderView(0)?.findViewById(R.id.right_drawer_list)
addFileReader()
- setupTabsAdapter()
setTableDrawerInfo()
- setTabListener()
activity?.let {
compatCallback = CompatFindActionModeCallback(it)
}
@@ -572,13 +506,6 @@ abstract class CoreReaderFragment :
loadPrefs()
updateTitle()
handleIntentExtras(requireActivity().intent)
- tabRecyclerView?.let {
- it.adapter = tabsAdapter
- tabCallback?.let { callBack ->
- ItemTouchHelper(callBack).attachToRecyclerView(it)
- }
- }
-
// Only check intent on first start of activity. Otherwise the intents will enter infinite loops
// when "Don't keep activities" is on.
if (savedInstanceState == null) {
@@ -700,50 +627,6 @@ abstract class CoreReaderFragment :
unsupportedMimeTypeHandler?.setAlertDialogShower(alertDialogShower as AlertDialogShower)
}
- private fun prepareViews() {
- fragmentReaderBinding?.let { readerBinding ->
- with(readerBinding.root) {
- activityMainRoot = findViewById(R.id.activity_main_root)
- toolbar = findViewById(R.id.toolbar)
- tabSwitcherRoot = findViewById(R.id.activity_main_tab_switcher)
- tabRecyclerView = findViewById(R.id.tab_switcher_recycler_view)
- donationLayout = findViewById(R.id.donation_layout)
- }
- }
- }
-
- private fun initTabCallback() {
- tabCallback = object : ItemTouchHelper.Callback() {
- override fun getMovementFlags(
- recyclerView: RecyclerView,
- viewHolder: RecyclerView.ViewHolder
- ): Int = makeMovementFlags(0, ItemTouchHelper.UP or ItemTouchHelper.DOWN)
-
- override fun onChildDraw(
- c: Canvas,
- recyclerView: RecyclerView,
- viewHolder: RecyclerView.ViewHolder,
- dX: Float,
- dY: Float,
- actionState: Int,
- isCurrentlyActive: Boolean
- ) {
- super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
- viewHolder.itemView.alpha = 1 - abs(dY) / viewHolder.itemView.measuredHeight
- }
-
- override fun onMove(
- recyclerView: RecyclerView,
- viewHolder: RecyclerView.ViewHolder,
- target: RecyclerView.ViewHolder
- ): Boolean = false
-
- override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
- closeTab(viewHolder.adapterPosition)
- }
- }
- }
-
@Suppress("MagicNumber")
private fun initHideBackToTopTimer() {
hideBackToTopTimer = object : CountDownTimer(1200, 1200) {
@@ -811,22 +694,6 @@ abstract class CoreReaderFragment :
})
}
- private fun setTabListener() {
- tabsAdapter?.setTabClickListener(object : TabsAdapter.TabClickListener {
- override fun onSelectTab(view: View, position: Int) {
- hideTabSwitcher()
- selectTab(position)
-
- // Bug Fix #592
- updateBottomToolbarArrowsAlpha()
- }
-
- override fun onCloseTab(view: View, position: Int) {
- closeTab(position)
- }
- })
- }
-
private fun setTableDrawerInfo() {
tableDrawerRight?.apply {
layoutManager = LinearLayoutManager(requireActivity())
@@ -836,22 +703,6 @@ abstract class CoreReaderFragment :
}
}
- private fun setupTabsAdapter() {
- tabsAdapter = painter?.let {
- TabsAdapter(
- requireActivity() as AppCompatActivity,
- webViewList,
- it
- ).apply {
- registerAdapterDataObserver(object : AdapterDataObserver() {
- override fun onChanged() {
- readerMenuState?.updateTabIcon(itemCount)
- }
- })
- }
- }
- }
-
private fun addFileReader() {
documentParserJs = requireActivity().readFile("js/documentParser.js")
documentSections?.clear()
@@ -901,7 +752,6 @@ abstract class CoreReaderFragment :
)
}
showSearchPlaceHolderInToolbar(true)
- startAnimation(tabSwitcherRoot, R.anim.slide_down)
readerMenuState?.showTabSwitcherOptions()
}
@@ -926,13 +776,6 @@ abstract class CoreReaderFragment :
}
}
- protected fun startAnimation(
- view: View?,
- @AnimRes anim: Int
- ) {
- view?.startAnimation(AnimationUtils.loadAnimation(view.context, anim))
- }
-
/**
* @param shouldCloseZimBook A flag to indicate whether the ZIM book should be closed.
* - Default is `true`, which ensures normal behavior for most scenarios.
@@ -1074,7 +917,7 @@ abstract class CoreReaderFragment :
@Suppress("ReturnCount", "NestedBlockDepth")
override fun onBackPressed(activity: AppCompatActivity): FragmentActivityExtensions.Super {
when {
- tabSwitcherRoot?.visibility == VISIBLE -> {
+ readerScreenState.value.showTabSwitcher -> {
selectTab(
if (currentWebViewIndex < webViewList.size) {
currentWebViewIndex
@@ -1300,16 +1143,11 @@ abstract class CoreReaderFragment :
}
safelyCancelBookmarkJob()
unBindViewsAndBinding()
- tabCallback = null
hideBackToTopTimer?.cancel()
hideBackToTopTimer = null
stopOngoingLoadingAndClearWebViewList()
- actionBar = null
- mainMenu = null
- tabRecyclerView?.adapter = null
tableDrawerRight?.adapter = null
tableDrawerAdapter = null
- tabsAdapter = null
tempWebViewListForUndo.clear()
// create a base Activity class that class this.
deleteCachedFiles(requireActivity())
@@ -1336,17 +1174,10 @@ abstract class CoreReaderFragment :
@SuppressLint("ClickableViewAccessibility")
private fun unBindViewsAndBinding() {
- activityMainRoot = null
- tabRecyclerView = null
- tabSwitcherRoot = null
compatCallback?.finish()
compatCallback = null
- toolbar?.setOnTouchListener(null)
- toolbar = null
drawerLayout = null
tableDrawerRightContainer = null
- fragmentReaderBinding?.root?.removeAllViews()
- fragmentReaderBinding = null
donationLayout?.removeAllViews()
donationLayout = null
}
@@ -1522,18 +1353,7 @@ abstract class CoreReaderFragment :
reopenBook()
}
tempWebViewForUndo?.let {
- if (tabSwitcherRoot?.visibility == GONE) {
- // Remove the top margin from the webView when the tabSwitcher is not visible.
- // We have added this margin in `TabsAdapter` to not show the top margin in tabs.
- // `tempWebViewForUndo` saved with that margin so before showing it to the `contentFrame`
- // We need to set full width and height for properly showing the content of webView.
- it.layoutParams = LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT
- )
- }
webViewList.add(index, it)
- tabsAdapter?.notifyDataSetChanged()
readerScreenState.value.snackBarHostState.snack(
context?.getString(R.string.tab_restored).orEmpty(),
lifecycleScope = lifecycleScope
@@ -1555,7 +1375,6 @@ abstract class CoreReaderFragment :
currentWebViewIndex = position
val webView = safelyGetWebView(position) ?: return
safelyAddWebView(webView)
- tabsAdapter?.selected = currentWebViewIndex
updateBottomToolbarVisibility()
loadPrefs()
updateUrlFlow()
@@ -1999,7 +1818,6 @@ abstract class CoreReaderFragment :
private fun restoreDeletedTabs() {
if (tempWebViewListForUndo.isNotEmpty()) {
webViewList.addAll(tempWebViewListForUndo)
- tabsAdapter?.notifyDataSetChanged()
readerScreenState.value.snackBarHostState.snack(
context?.getString(R.string.tabs_restored).orEmpty(),
lifecycleScope = lifecycleScope
@@ -2397,10 +2215,8 @@ abstract class CoreReaderFragment :
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
- // Forcing redraw of RecyclerView children so that the tabs are properly oriented on rotation
- tabRecyclerView?.adapter = tabsAdapter
// force redraw of donation layout if it is showing.
- if (donationLayout?.isVisible == true) {
+ if (readerScreenState.value.shouldShowDonationPopup) {
showDonationLayout()
}
}
@@ -2647,7 +2463,6 @@ abstract class CoreReaderFragment :
return
}
updateTableOfContents()
- tabsAdapter?.notifyDataSetChanged()
updateBottomToolbarArrowsAlpha()
val zimFileReader = zimReaderContainer?.zimFileReader
if (hasValidFileAndUrl(getCurrentWebView()?.url, zimFileReader)) {
@@ -2681,13 +2496,19 @@ abstract class CoreReaderFragment :
private fun hasValidFileAndUrl(url: String?, zimFileReader: ZimFileReader?): Boolean =
url != null && zimFileReader != null
- override fun webViewFailedLoading(url: String) {
+ override fun webViewFailedLoading(failingUrl: String) {
if (isAdded) {
// If a URL fails to load, update the bookmark toggle.
// This fixes the scenario where the previous page is bookmarked and the next
// page fails to load, ensuring the bookmark toggle is unset correctly.
updateUrlFlow()
- Log.d(TAG_KIWIX, String.format(getString(R.string.error_article_url_not_found), url))
+ Log.d(
+ TAG_KIWIX,
+ String.format(
+ getString(R.string.error_article_url_not_found),
+ failingUrl
+ )
+ )
}
}
@@ -2707,7 +2528,6 @@ abstract class CoreReaderFragment :
override fun webViewTitleUpdated(title: String) {
updateTabIcon(webViewList.size)
- tabsAdapter?.notifyDataSetChanged()
}
@Suppress("MagicNumber")
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt
index 9a9f01f8b..c8cb7cd7c 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/main/reader/ReaderScreen.kt
@@ -89,7 +89,6 @@ import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
@@ -97,6 +96,8 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
@@ -146,6 +147,10 @@ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TWO_DP
import org.kiwix.kiwixmobile.core.utils.StyleUtils.fromHtml
const val CONTENT_LOADING_PROGRESSBAR_TESTING_TAG = "contentLoadingProgressBarTestingTag"
+const val TAB_SWITCHER_VIEW_TESTING_TAG = "tabSwitcherViewTestingTag"
+const val READER_SCREEN_TESTING_TAG = "readerScreenTestingTag"
+const val CLOSE_ALL_TABS_BUTTON_TESTING_TAG = "closeAllTabsButtonTestingTag"
+const val TAB_TITLE_TESTING_TAG = "tabTitleTestingTag"
@OptIn(ExperimentalMaterial3Api::class)
@Suppress("ComposableLambdaParameterNaming")
@@ -157,10 +162,10 @@ fun ReaderScreen(
navigationIcon: @Composable () -> Unit
) {
val bottomNavHeightInDp = with(LocalDensity.current) { state.bottomNavigationHeight.toDp() }
- val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
+ val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
LaunchedEffect(bottomAppBarScrollBehavior.state.heightOffset) {
- onBottomScrollOffsetChanged(scrollBehavior.state.heightOffset)
+ onBottomScrollOffsetChanged(bottomAppBarScrollBehavior.state.heightOffset)
}
KiwixDialogTheme {
Scaffold(
@@ -169,21 +174,21 @@ fun ReaderScreen(
ReaderTopBar(
state,
actionMenuItems,
- scrollBehavior,
+ topAppBarScrollBehavior,
navigationIcon
)
},
floatingActionButton = { BackToTopFab(state) },
modifier = Modifier
.systemBarsPadding()
- .nestedScroll(scrollBehavior.nestedScrollConnection)
- .nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection)
.padding(bottom = bottomNavHeightInDp)
+ .semantics { testTag = READER_SCREEN_TESTING_TAG }
) { paddingValues ->
ReaderContentLayout(
state,
Modifier.padding(paddingValues),
- bottomAppBarScrollBehavior
+ bottomAppBarScrollBehavior,
+ topAppBarScrollBehavior
)
}
}
@@ -195,7 +200,7 @@ fun ReaderScreen(
private fun ReaderTopBar(
state: ReaderScreenState,
actionMenuItems: List,
- scrollBehavior: TopAppBarScrollBehavior,
+ topAppBarScrollBehavior: TopAppBarScrollBehavior,
navigationIcon: @Composable () -> Unit,
) {
if (!state.shouldShowFullScreenMode && !state.fullScreenItem.first) {
@@ -203,7 +208,7 @@ private fun ReaderTopBar(
title = if (state.showTabSwitcher) "" else state.readerScreenTitle,
navigationIcon = navigationIcon,
actionMenuItems = actionMenuItems,
- topAppBarScrollBehavior = scrollBehavior,
+ topAppBarScrollBehavior = topAppBarScrollBehavior,
searchBar =
searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps)
)
@@ -215,7 +220,8 @@ private fun ReaderTopBar(
private fun ReaderContentLayout(
state: ReaderScreenState,
modifier: Modifier = Modifier,
- bottomAppBarScrollBehavior: BottomAppBarScrollBehavior
+ bottomAppBarScrollBehavior: BottomAppBarScrollBehavior,
+ topAppBarScrollBehavior: TopAppBarScrollBehavior
) {
Box(modifier = modifier.fillMaxSize()) {
TabSwitcherAnimated(state)
@@ -225,7 +231,7 @@ private fun ReaderContentLayout(
state.fullScreenItem.first -> ShowFullScreenView(state)
else -> {
- ShowZIMFileContent(state)
+ ShowZIMFileContent(state, bottomAppBarScrollBehavior, topAppBarScrollBehavior)
ShowProgressBarIfZIMFilePageIsLoading(state)
Column(Modifier.align(Alignment.BottomCenter)) {
TtsControls(state)
@@ -255,11 +261,11 @@ private fun TabSwitcherAnimated(state: ReaderScreenState) {
val transitionSpec = remember {
slideInVertically(
initialOffsetY = { -it },
- animationSpec = tween(durationMillis = 300)
+ animationSpec = tween(durationMillis = HIDE_TAB_SWITCHER_DELAY.toInt())
) + fadeIn() togetherWith
slideOutVertically(
targetOffsetY = { -it },
- animationSpec = tween(durationMillis = 300)
+ animationSpec = tween(durationMillis = HIDE_TAB_SWITCHER_DELAY.toInt())
) + fadeOut()
}
@@ -267,7 +273,9 @@ private fun TabSwitcherAnimated(state: ReaderScreenState) {
visible = state.showTabSwitcher,
enter = transitionSpec.targetContentEnter,
exit = transitionSpec.initialContentExit,
- modifier = Modifier.zIndex(1f),
+ modifier = Modifier
+ .zIndex(1f)
+ .semantics { testTag = TAB_SWITCHER_VIEW_TESTING_TAG },
) {
TabSwitcherView(
state.kiwixWebViewList,
@@ -349,8 +357,13 @@ private fun BoxScope.CloseFullScreenImageButton(
}
}
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
-private fun ShowZIMFileContent(state: ReaderScreenState) {
+private fun ShowZIMFileContent(
+ state: ReaderScreenState,
+ bottomAppBarScrollBehavior: BottomAppBarScrollBehavior,
+ topAppBarScrollBehavior: TopAppBarScrollBehavior
+) {
state.selectedWebView?.let { selectedWebView ->
key(selectedWebView) {
AndroidView(
@@ -359,14 +372,23 @@ private fun ShowZIMFileContent(state: ReaderScreenState) {
FrameLayout(context).apply {
// Ensure the WebView has no parent before adding
(selectedWebView.parent as? ViewGroup)?.removeView(selectedWebView)
+ selectedWebView.setOnScrollChangeListener(null)
+ selectedWebView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
+ val deltaY = (scrollY - oldScrollY).toFloat()
+ if (deltaY == 0f) return@setOnScrollChangeListener
+ // SAFELY drive top and bottom app bars
+ topAppBarScrollBehavior.state.heightOffset -= deltaY
+ bottomAppBarScrollBehavior.state.heightOffset -= deltaY
+ }
+ selectedWebView.layoutParams = FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.MATCH_PARENT
+ )
addView(selectedWebView)
}
},
- modifier = Modifier
- .fillMaxSize()
- .verticalScroll(rememberScrollState())
+ modifier = Modifier.fillMaxSize()
)
- // TODO handle the unnecessary vertical scroll when webView page chnaged.
}
}
}
@@ -657,6 +679,7 @@ private fun BoxScope.CloseAllTabButton(onCloseAllTabs: () -> Unit) {
.graphicsLayer {
rotationZ = rotation
}
+ .semantics { testTag = CLOSE_ALL_TABS_BUTTON_TESTING_TAG }
.clickable(
enabled = !isAnimating,
onClick = {
@@ -732,7 +755,8 @@ private fun TabItemHeader(
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.padding(end = FOUR_DP)
- .weight(1f),
+ .weight(1f)
+ .semantics { testTag = TAB_TITLE_TESTING_TAG },
style = MaterialTheme.typography.labelSmall
)
IconButton(
diff --git a/core/src/main/res/layout/fragment_reader.xml b/core/src/main/res/layout/fragment_reader.xml
deleted file mode 100644
index e339ee94c..000000000
--- a/core/src/main/res/layout/fragment_reader.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/src/main/res/layout/ic_tab_switcher.xml b/core/src/main/res/layout/ic_tab_switcher.xml
deleted file mode 100644
index 45e0b1d6f..000000000
--- a/core/src/main/res/layout/ic_tab_switcher.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
diff --git a/core/src/main/res/layout/layout_scrolling_toolbar.xml b/core/src/main/res/layout/layout_scrolling_toolbar.xml
deleted file mode 100644
index cec0fad07..000000000
--- a/core/src/main/res/layout/layout_scrolling_toolbar.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
-
-
-
diff --git a/core/src/main/res/layout/layout_standard_app_bar.xml b/core/src/main/res/layout/layout_standard_app_bar.xml
deleted file mode 100644
index 9cef6de5a..000000000
--- a/core/src/main/res/layout/layout_standard_app_bar.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
diff --git a/core/src/main/res/layout/layout_toolbar.xml b/core/src/main/res/layout/layout_toolbar.xml
deleted file mode 100644
index ab4b33c49..000000000
--- a/core/src/main/res/layout/layout_toolbar.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/src/main/res/layout/reader_fragment_content.xml b/core/src/main/res/layout/reader_fragment_content.xml
deleted file mode 100644
index 93053de0b..000000000
--- a/core/src/main/res/layout/reader_fragment_content.xml
+++ /dev/null
@@ -1,145 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/src/main/res/layout/tab_switcher.xml b/core/src/main/res/layout/tab_switcher.xml
deleted file mode 100644
index d242d5acf..000000000
--- a/core/src/main/res/layout/tab_switcher.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
-
-
-
-
diff --git a/core/src/main/res/menu/menu_main.xml b/core/src/main/res/menu/menu_main.xml
deleted file mode 100644
index 2d20af056..000000000
--- a/core/src/main/res/menu/menu_main.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-
diff --git a/core/src/main/res/menu/menu_webview.xml b/core/src/main/res/menu/menu_webview.xml
index ff99b82d0..33a88fc66 100644
--- a/core/src/main/res/menu/menu_webview.xml
+++ b/core/src/main/res/menu/menu_webview.xml
@@ -13,14 +13,16 @@
limitations under the License.
-->
-