mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
Fixed: An extra space appeared at the bottom when navigating to a ZIM page with greater height and then returning to the previous page.
* Removed unnecessary code and files from the project. * Refactored several UI test cases to align with the Compose UI. * Fixed: Some lint issues.
This commit is contained in:
parent
db22e245b7
commit
a51615e249
@ -109,7 +109,7 @@ class TopLevelDestinationTest : BaseActivityTest() {
|
|||||||
fun testTopLevelDestination() {
|
fun testTopLevelDestination() {
|
||||||
topLevel {
|
topLevel {
|
||||||
clickReaderOnBottomNav {
|
clickReaderOnBottomNav {
|
||||||
assertReaderScreenDisplayed()
|
assertReaderScreenDisplayed(composeTestRule)
|
||||||
}
|
}
|
||||||
clickDownloadOnBottomNav {
|
clickDownloadOnBottomNav {
|
||||||
onlineLibrary {
|
onlineLibrary {
|
||||||
|
@ -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
|
||||||
import org.kiwix.kiwixmobile.testutils.TestUtils.refresh
|
import org.kiwix.kiwixmobile.testutils.TestUtils.refresh
|
||||||
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
||||||
|
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
|
||||||
import org.kiwix.kiwixmobile.ui.BOOK_ITEM_TESTING_TAG
|
import org.kiwix.kiwixmobile.ui.BOOK_ITEM_TESTING_TAG
|
||||||
|
|
||||||
fun library(func: LibraryRobot.() -> Unit) = LibraryRobot().applyWithViewHierarchyPrinting(func)
|
fun library(func: LibraryRobot.() -> Unit) = LibraryRobot().applyWithViewHierarchyPrinting(func)
|
||||||
@ -102,8 +103,8 @@ class LibraryRobot : BaseRobot() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun waitUntilZimFilesRefreshing(composeTestRule: ComposeContentTestRule) {
|
fun waitUntilZimFilesRefreshing(composeTestRule: ComposeContentTestRule) {
|
||||||
pauseForBetterTestPerformance()
|
|
||||||
testFlakyView({
|
testFlakyView({
|
||||||
|
composeTestRule.waitUntilTimeout()
|
||||||
composeTestRule.onNodeWithTag(CONTENT_LOADING_PROGRESSBAR_TESTING_TAG)
|
composeTestRule.onNodeWithTag(CONTENT_LOADING_PROGRESSBAR_TESTING_TAG)
|
||||||
.assertIsNotDisplayed()
|
.assertIsNotDisplayed()
|
||||||
})
|
})
|
||||||
|
@ -18,15 +18,20 @@
|
|||||||
|
|
||||||
package org.kiwix.kiwixmobile.nav.destination.reader
|
package org.kiwix.kiwixmobile.nav.destination.reader
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||||
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import applyWithViewHierarchyPrinting
|
import applyWithViewHierarchyPrinting
|
||||||
import org.kiwix.kiwixmobile.BaseRobot
|
import org.kiwix.kiwixmobile.BaseRobot
|
||||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
import org.kiwix.kiwixmobile.core.main.reader.READER_SCREEN_TESTING_TAG
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
|
||||||
|
|
||||||
fun reader(func: ReaderRobot.() -> Unit) = ReaderRobot().applyWithViewHierarchyPrinting(func)
|
fun reader(func: ReaderRobot.() -> Unit) = ReaderRobot().applyWithViewHierarchyPrinting(func)
|
||||||
|
|
||||||
class ReaderRobot : BaseRobot() {
|
class ReaderRobot : BaseRobot() {
|
||||||
fun assertReaderScreenDisplayed() {
|
fun assertReaderScreenDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||||
isVisible(ViewId(R.id.activity_main_root))
|
composeTestRule.apply {
|
||||||
|
waitUntilTimeout()
|
||||||
|
onNodeWithTag(READER_SCREEN_TESTING_TAG).assertExists()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,11 +20,13 @@ package org.kiwix.kiwixmobile.page.history
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.ui.test.assertTextEquals
|
import androidx.compose.ui.test.assertTextEquals
|
||||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
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.onNodeWithTag
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.compose.ui.test.performTouchInput
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
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.matcher.ViewMatchers.withId
|
||||||
import androidx.test.espresso.web.sugar.Web.onWebView
|
import androidx.test.espresso.web.sugar.Web.onWebView
|
||||||
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
|
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.BaseRobot
|
||||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
import org.kiwix.kiwixmobile.Findable.ViewId
|
||||||
import org.kiwix.kiwixmobile.core.R
|
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.page.DELETE_MENU_ICON_TESTING_TAG
|
||||||
import org.kiwix.kiwixmobile.core.ui.components.TOOLBAR_TITLE_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.core.utils.dialog.ALERT_DIALOG_TITLE_TEXT_TESTING_TAG
|
||||||
import org.kiwix.kiwixmobile.testutils.TestUtils
|
import org.kiwix.kiwixmobile.testutils.TestUtils
|
||||||
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
||||||
|
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
|
||||||
|
|
||||||
fun navigationHistory(func: NavigationHistoryRobot.() -> Unit) =
|
fun navigationHistory(func: NavigationHistoryRobot.() -> Unit) =
|
||||||
NavigationHistoryRobot().applyWithViewHierarchyPrinting(func)
|
NavigationHistoryRobot().applyWithViewHierarchyPrinting(func)
|
||||||
@ -55,12 +59,14 @@ class NavigationHistoryRobot : BaseRobot() {
|
|||||||
isVisible(ViewId(readerFragment))
|
isVisible(ViewId(readerFragment))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun closeTabSwitcherIfVisible() {
|
fun closeTabSwitcherIfVisible(composeTestRule: ComposeContentTestRule) {
|
||||||
try {
|
try {
|
||||||
pauseForBetterTestPerformance()
|
composeTestRule.apply {
|
||||||
isVisible(ViewId(R.id.tab_switcher_close_all_tabs))
|
waitUntilTimeout()
|
||||||
pressBack()
|
onNodeWithTag(TAB_SWITCHER_VIEW_TESTING_TAG).assertExists()
|
||||||
} catch (_: Exception) {
|
pressBack()
|
||||||
|
}
|
||||||
|
} catch (_: AssertionError) {
|
||||||
Log.i(
|
Log.i(
|
||||||
"NAVIGATION_HISTORY_TEST",
|
"NAVIGATION_HISTORY_TEST",
|
||||||
"Couldn't found tab switcher, probably it is not visible"
|
"Couldn't found tab switcher, probably it is not visible"
|
||||||
@ -91,14 +97,24 @@ class NavigationHistoryRobot : BaseRobot() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun longClickOnBackwardButton() {
|
fun longClickOnBackwardButton(composeTestRule: ComposeContentTestRule) {
|
||||||
pauseForBetterTestPerformance()
|
composeTestRule.apply {
|
||||||
testFlakyView({ onView(withId(R.id.bottom_toolbar_arrow_back)).perform(longClick()) })
|
waitUntilTimeout()
|
||||||
|
testFlakyView({
|
||||||
|
onNodeWithContentDescription(context.getString(R.string.go_to_previous_page))
|
||||||
|
.performTouchInput { longClick() }
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun longClickOnForwardButton() {
|
fun longClickOnForwardButton(composeTestRule: ComposeContentTestRule) {
|
||||||
pauseForBetterTestPerformance()
|
composeTestRule.apply {
|
||||||
longClickOn(ViewId(R.id.bottom_toolbar_arrow_forward))
|
waitUntilTimeout()
|
||||||
|
testFlakyView({
|
||||||
|
onNodeWithContentDescription(context.getString(R.string.go_to_next_page))
|
||||||
|
.performTouchInput { longClick() }
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertBackwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
|
fun assertBackwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||||
@ -117,9 +133,12 @@ class NavigationHistoryRobot : BaseRobot() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnBackwardButton() {
|
fun clickOnBackwardButton(composeTestRule: ComposeContentTestRule) {
|
||||||
pauseForBetterTestPerformance()
|
composeTestRule.apply {
|
||||||
clickOn(ViewId(R.id.bottom_toolbar_arrow_back))
|
waitUntilTimeout()
|
||||||
|
onNodeWithContentDescription(context.getString(R.string.go_to_previous_page))
|
||||||
|
.performClick()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertForwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
|
fun assertForwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||||
|
@ -147,14 +147,14 @@ class NavigationHistoryTest : BaseActivityTest() {
|
|||||||
}
|
}
|
||||||
StandardActions.closeDrawer() // close the drawer if open before running the test cases.
|
StandardActions.closeDrawer() // close the drawer if open before running the test cases.
|
||||||
navigationHistory {
|
navigationHistory {
|
||||||
closeTabSwitcherIfVisible()
|
closeTabSwitcherIfVisible(composeTestRule)
|
||||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||||
clickOnAndroidArticle()
|
clickOnAndroidArticle()
|
||||||
longClickOnBackwardButton()
|
longClickOnBackwardButton(composeTestRule)
|
||||||
assertBackwardNavigationHistoryDialogDisplayed(composeTestRule)
|
assertBackwardNavigationHistoryDialogDisplayed(composeTestRule)
|
||||||
pressBack()
|
pressBack()
|
||||||
clickOnBackwardButton()
|
clickOnBackwardButton(composeTestRule)
|
||||||
longClickOnForwardButton()
|
longClickOnForwardButton(composeTestRule)
|
||||||
assertForwardNavigationHistoryDialogDisplayed(composeTestRule)
|
assertForwardNavigationHistoryDialogDisplayed(composeTestRule)
|
||||||
clickOnDeleteHistory(composeTestRule)
|
clickOnDeleteHistory(composeTestRule)
|
||||||
assertDeleteDialogDisplayed(composeTestRule)
|
assertDeleteDialogDisplayed(composeTestRule)
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package org.kiwix.kiwixmobile.reader
|
package org.kiwix.kiwixmobile.reader
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import androidx.compose.ui.test.junit4.createComposeRule
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
@ -46,6 +47,7 @@ import org.kiwix.kiwixmobile.BaseActivityTest
|
|||||||
import org.kiwix.kiwixmobile.R
|
import org.kiwix.kiwixmobile.R
|
||||||
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
|
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
|
||||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
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.core.utils.TestingUtils.RETRY_RULE_ORDER
|
||||||
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
||||||
import org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragmentDirections
|
import org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragmentDirections
|
||||||
@ -64,6 +66,9 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
|
|||||||
@JvmField
|
@JvmField
|
||||||
val retryRule = RetryRule()
|
val retryRule = RetryRule()
|
||||||
|
|
||||||
|
@get:Rule(order = COMPOSE_TEST_RULE_ORDER)
|
||||||
|
val composeTestRule = createComposeRule()
|
||||||
|
|
||||||
private lateinit var kiwixMainActivity: KiwixMainActivity
|
private lateinit var kiwixMainActivity: KiwixMainActivity
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@ -136,10 +141,10 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
|
|||||||
openKiwixReaderFragmentWithFile(zimFile)
|
openKiwixReaderFragmentWithFile(zimFile)
|
||||||
reader {
|
reader {
|
||||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||||
clickOnTabIcon()
|
clickOnTabIcon(composeTestRule)
|
||||||
clickOnClosedAllTabsButton()
|
clickOnClosedAllTabsButton(composeTestRule)
|
||||||
clickOnUndoButton()
|
clickOnUndoButton(composeTestRule)
|
||||||
assertTabRestored()
|
assertTabRestored(composeTestRule)
|
||||||
pressBack()
|
pressBack()
|
||||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||||
}
|
}
|
||||||
@ -171,6 +176,8 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
|
|||||||
openKiwixReaderFragmentWithFile(downloadingZimFile)
|
openKiwixReaderFragmentWithFile(downloadingZimFile)
|
||||||
reader {
|
reader {
|
||||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||||
|
clickOnTabIcon(composeTestRule)
|
||||||
|
clickOnTabIcon(composeTestRule)
|
||||||
// test the whole welcome page is loaded or not
|
// test the whole welcome page is loaded or not
|
||||||
assertArticleLoaded("Hydrogène")
|
assertArticleLoaded("Hydrogène")
|
||||||
assertArticleLoaded("Automobile")
|
assertArticleLoaded("Automobile")
|
||||||
|
@ -18,10 +18,12 @@
|
|||||||
|
|
||||||
package org.kiwix.kiwixmobile.reader
|
package org.kiwix.kiwixmobile.reader
|
||||||
|
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.compose.ui.test.assertTextEquals
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
import androidx.compose.ui.test.onAllNodesWithTag
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
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.sugar.Web.onWebView
|
||||||
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
|
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
|
||||||
import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
|
import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
|
||||||
@ -29,11 +31,13 @@ import androidx.test.espresso.web.webdriver.Locator
|
|||||||
import applyWithViewHierarchyPrinting
|
import applyWithViewHierarchyPrinting
|
||||||
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
|
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
|
||||||
import org.kiwix.kiwixmobile.BaseRobot
|
import org.kiwix.kiwixmobile.BaseRobot
|
||||||
import org.kiwix.kiwixmobile.Findable.Text
|
|
||||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
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
|
||||||
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
||||||
|
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
|
||||||
|
|
||||||
fun reader(func: ReaderRobot.() -> Unit) = ReaderRobot().applyWithViewHierarchyPrinting(func)
|
fun reader(func: ReaderRobot.() -> Unit) = ReaderRobot().applyWithViewHierarchyPrinting(func)
|
||||||
|
|
||||||
@ -45,30 +49,43 @@ class ReaderRobot : BaseRobot() {
|
|||||||
isVisible(ViewId(readerFragment))
|
isVisible(ViewId(readerFragment))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnTabIcon() {
|
fun clickOnTabIcon(composeTestRule: ComposeContentTestRule) {
|
||||||
pauseForBetterTestPerformance()
|
composeTestRule.apply {
|
||||||
testFlakyView({ onView(withId(R.id.ic_tab_switcher_text)).perform(click()) })
|
waitUntilTimeout()
|
||||||
|
testFlakyView({
|
||||||
|
onNodeWithTag(TAB_MENU_ITEM_TESTING_TAG).performClick()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnClosedAllTabsButton() {
|
fun clickOnClosedAllTabsButton(composeTestRule: ComposeContentTestRule) {
|
||||||
pauseForBetterTestPerformance()
|
composeTestRule.apply {
|
||||||
clickOn(ViewId(R.id.tab_switcher_close_all_tabs))
|
waitUntilTimeout()
|
||||||
|
testFlakyView({
|
||||||
|
onNodeWithTag(CLOSE_ALL_TABS_BUTTON_TESTING_TAG).performClick()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnUndoButton() {
|
fun clickOnUndoButton(composeTestRule: ComposeContentTestRule) {
|
||||||
try {
|
try {
|
||||||
onView(withText("UNDO")).perform(click())
|
composeTestRule.apply {
|
||||||
} catch (runtimeException: RuntimeException) {
|
onNodeWithText("UNDO", useUnmergedTree = true)
|
||||||
|
.performClick()
|
||||||
|
}
|
||||||
|
} catch (_: AssertionError) {
|
||||||
if (retryCountForClickOnUndoButton > 0) {
|
if (retryCountForClickOnUndoButton > 0) {
|
||||||
retryCountForClickOnUndoButton--
|
retryCountForClickOnUndoButton--
|
||||||
clickOnUndoButton()
|
clickOnUndoButton(composeTestRule)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertTabRestored() {
|
fun assertTabRestored(composeTestRule: ComposeContentTestRule) {
|
||||||
pauseForBetterTestPerformance()
|
composeTestRule.apply {
|
||||||
isVisible(Text("Test Zim"))
|
waitUntilTimeout()
|
||||||
|
onAllNodesWithTag(TAB_TITLE_TESTING_TAG)[0].assertTextEquals("Test Zim")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun pauseForBetterTestPerformance() {
|
private fun pauseForBetterTestPerformance() {
|
||||||
|
@ -27,9 +27,6 @@ import androidx.compose.ui.test.onNodeWithTag
|
|||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
import androidx.compose.ui.test.performTextClearance
|
import androidx.compose.ui.test.performTextClearance
|
||||||
import androidx.compose.ui.test.performTextInput
|
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.sugar.Web.onWebView
|
||||||
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
|
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
|
||||||
import androidx.test.espresso.web.webdriver.Locator
|
import androidx.test.espresso.web.webdriver.Locator
|
||||||
@ -38,7 +35,7 @@ import applyWithViewHierarchyPrinting
|
|||||||
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
|
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
|
||||||
import org.kiwix.kiwixmobile.BaseRobot
|
import org.kiwix.kiwixmobile.BaseRobot
|
||||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
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_FIELD_TESTING_TAG
|
||||||
import org.kiwix.kiwixmobile.core.search.SEARCH_ITEM_TESTING_TAG
|
import org.kiwix.kiwixmobile.core.search.SEARCH_ITEM_TESTING_TAG
|
||||||
import org.kiwix.kiwixmobile.core.ui.components.NAVIGATION_ICON_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()
|
composeTestRule.onNodeWithTag(NAVIGATION_ICON_TESTING_TAG).performClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openSearchScreen() {
|
private fun openSearchScreen(composeTestRule: ComposeContentTestRule) {
|
||||||
testFlakyView({ onView(withId(R.id.menu_search)).perform(click()) })
|
testFlakyView(
|
||||||
|
{
|
||||||
|
composeTestRule.onNodeWithTag(SEARCH_ICON_TESTING_TAG).performClick()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun searchAndClickOnArticle(searchString: String, composeTestRule: ComposeContentTestRule) {
|
fun searchAndClickOnArticle(searchString: String, composeTestRule: ComposeContentTestRule) {
|
||||||
openSearchScreen()
|
openSearchScreen(composeTestRule)
|
||||||
searchWithFrequentlyTypedWords(searchString, composeTestRule = composeTestRule)
|
searchWithFrequentlyTypedWords(searchString, composeTestRule = composeTestRule)
|
||||||
clickOnSearchItemInSearchList(composeTestRule)
|
clickOnSearchItemInSearchList(composeTestRule)
|
||||||
checkZimFileSearchSuccessful(org.kiwix.kiwixmobile.R.id.readerFragment)
|
checkZimFileSearchSuccessful(org.kiwix.kiwixmobile.R.id.readerFragment)
|
||||||
|
@ -128,7 +128,7 @@ class GetContentShortcutTest {
|
|||||||
onlineLibrary { assertOnlineLibraryFragmentDisplayed(composeTestRule) }
|
onlineLibrary { assertOnlineLibraryFragmentDisplayed(composeTestRule) }
|
||||||
topLevel {
|
topLevel {
|
||||||
clickReaderOnBottomNav {
|
clickReaderOnBottomNav {
|
||||||
assertReaderScreenDisplayed()
|
assertReaderScreenDisplayed(composeTestRule)
|
||||||
}
|
}
|
||||||
clickDownloadOnBottomNav {
|
clickDownloadOnBottomNav {
|
||||||
onlineLibrary { assertOnlineLibraryFragmentDisplayed(composeTestRule) }
|
onlineLibrary { assertOnlineLibraryFragmentDisplayed(composeTestRule) }
|
||||||
|
@ -48,6 +48,7 @@ import org.kiwix.kiwixmobile.core.extensions.toast
|
|||||||
import org.kiwix.kiwixmobile.core.extensions.update
|
import org.kiwix.kiwixmobile.core.extensions.update
|
||||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||||
import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
|
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
|
||||||
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromExternalLaunch
|
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromExternalLaunch
|
||||||
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromSearchScreen
|
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 org.kiwix.kiwixmobile.core.utils.files.Log
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
private const val HIDE_TAB_SWITCHER_DELAY: Long = 300
|
|
||||||
|
|
||||||
class KiwixReaderFragment : CoreReaderFragment() {
|
class KiwixReaderFragment : CoreReaderFragment() {
|
||||||
private var isFullScreenVideo: Boolean = false
|
private var isFullScreenVideo: Boolean = false
|
||||||
|
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_language_search"
|
|
||||||
android:icon="@drawable/action_search"
|
|
||||||
android:title="@string/search_label"
|
|
||||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
|
||||||
app:iconifiedByDefault="true"
|
|
||||||
app:showAsAction="ifRoom|collapseActionView" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_language_save"
|
|
||||||
android:icon="@drawable/ic_check_white_24dp"
|
|
||||||
android:title="@string/save_languages"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
</menu>
|
|
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/action_search"
|
|
||||||
android:icon="@drawable/action_search"
|
|
||||||
android:title="@string/search_label"
|
|
||||||
android:visible="true"
|
|
||||||
app:actionViewClass="androidx.appcompat.widget.SearchView"
|
|
||||||
app:iconifiedByDefault="true"
|
|
||||||
app:showAsAction="always|collapseActionView" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/select_language"
|
|
||||||
android:icon="@drawable/ic_language_white_24dp"
|
|
||||||
android:title="@string/pref_language_chooser"
|
|
||||||
android:visible="true"
|
|
||||||
app:showAsAction="ifRoom" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/get_zim_nearby_device"
|
|
||||||
android:icon="@drawable/ic_baseline_mobile_screen_share_24px"
|
|
||||||
android:title="@string/get_content_from_nearby_device"
|
|
||||||
app:showAsAction="always" />
|
|
||||||
|
|
||||||
</menu>
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
tools:context="org.kiwix.kiwixmobile.localFileTransfer.LocalFileTransferFragment">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_item_search_devices"
|
|
||||||
android:title="@string/search_for_peers"
|
|
||||||
app:showAsAction="always"
|
|
||||||
android:icon="@drawable/action_search" />
|
|
||||||
|
|
||||||
</menu>
|
|
@ -17,19 +17,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.kiwix.kiwixmobile.core.di.modules
|
package org.kiwix.kiwixmobile.core.di.modules
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.view.Menu
|
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import org.kiwix.kiwixmobile.core.data.DataSource
|
import org.kiwix.kiwixmobile.core.data.DataSource
|
||||||
import org.kiwix.kiwixmobile.core.di.ActivityScope
|
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.main.MainRepositoryActions
|
||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
|
||||||
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
||||||
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
|
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
|
||||||
|
|
||||||
@ -44,33 +37,5 @@ abstract class ActivityModule {
|
|||||||
@ActivityScope
|
@ActivityScope
|
||||||
fun providesMainPresenter(dataSource: DataSource): MainRepositoryActions =
|
fun providesMainPresenter(dataSource: DataSource): MainRepositoryActions =
|
||||||
MainRepositoryActions(dataSource)
|
MainRepositoryActions(dataSource)
|
||||||
|
|
||||||
@Provides
|
|
||||||
@ActivityScope
|
|
||||||
fun providesMainMenuFactory(
|
|
||||||
activity: Activity,
|
|
||||||
zimReaderContainer: ZimReaderContainer
|
|
||||||
): MainMenu.Factory = object : Factory {
|
|
||||||
override fun create(
|
|
||||||
menu: Menu,
|
|
||||||
webViews: MutableList<KiwixWebView>,
|
|
||||||
urlIsValid: Boolean,
|
|
||||||
menuClickListener: MenuClickListener,
|
|
||||||
disableReadAloud: Boolean,
|
|
||||||
disableTabs: Boolean,
|
|
||||||
disableSearch: Boolean
|
|
||||||
): MainMenu =
|
|
||||||
MainMenu(
|
|
||||||
activity,
|
|
||||||
zimReaderContainer.zimFileReader,
|
|
||||||
menu,
|
|
||||||
webViews,
|
|
||||||
urlIsValid,
|
|
||||||
disableReadAloud,
|
|
||||||
disableTabs,
|
|
||||||
disableSearch,
|
|
||||||
menuClickListener
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,194 +0,0 @@
|
|||||||
/*
|
|
||||||
* Kiwix Android
|
|
||||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
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<KiwixWebView>,
|
|
||||||
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<KiwixWebView>,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,200 +0,0 @@
|
|||||||
/*
|
|
||||||
* Kiwix Android
|
|
||||||
* Copyright (c) 2019 Kiwix <android.kiwix.org>
|
|
||||||
* 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 <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
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<KiwixWebView>,
|
|
||||||
private val painter: DarkModeViewPainter
|
|
||||||
) : RecyclerView.Adapter<TabsAdapter.ViewHolder>() {
|
|
||||||
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)
|
|
||||||
}
|
|
@ -27,7 +27,6 @@ import android.content.ServiceConnection
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.media.AudioManager.OnAudioFocusChangeListener
|
import android.media.AudioManager.OnAudioFocusChangeListener
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
@ -44,24 +43,18 @@ import android.view.Gravity.BOTTOM
|
|||||||
import android.view.Gravity.CENTER_HORIZONTAL
|
import android.view.Gravity.CENTER_HORIZONTAL
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MotionEvent
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.View.GONE
|
import android.view.View.GONE
|
||||||
import android.view.View.VISIBLE
|
import android.view.View.VISIBLE
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.animation.AnimationUtils
|
|
||||||
import android.webkit.WebBackForwardList
|
import android.webkit.WebBackForwardList
|
||||||
import android.webkit.WebView
|
import android.webkit.WebView
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.AnimRes
|
|
||||||
import androidx.appcompat.app.ActionBar
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.appcompat.widget.Toolbar
|
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Menu
|
import androidx.compose.material.icons.filled.Menu
|
||||||
import androidx.compose.material3.SnackbarDuration
|
import androidx.compose.material3.SnackbarDuration
|
||||||
@ -83,10 +76,8 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.drawerlayout.widget.DrawerLayout
|
import androidx.drawerlayout.widget.DrawerLayout
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
|
|
||||||
import com.google.android.material.bottomappbar.BottomAppBar
|
import com.google.android.material.bottomappbar.BottomAppBar
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import com.google.android.material.navigation.NavigationView
|
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.base.FragmentActivityExtensions
|
||||||
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
|
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
|
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.downloader.downloadManager.ZERO
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.consumeObservable
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.consumeObservable
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isLandScapeMode
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isLandScapeMode
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.observeNavigationResult
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.observeNavigationResult
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.requestNotificationPermission
|
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.closeFullScreenMode
|
||||||
import org.kiwix.kiwixmobile.core.extensions.showFullScreenMode
|
import org.kiwix.kiwixmobile.core.extensions.showFullScreenMode
|
||||||
import org.kiwix.kiwixmobile.core.extensions.snack
|
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.OnInitSucceedListener
|
||||||
import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech.OnSpeakingListener
|
import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech.OnSpeakingListener
|
||||||
import org.kiwix.kiwixmobile.core.main.KiwixWebView
|
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.MainRepositoryActions
|
||||||
import org.kiwix.kiwixmobile.core.main.OnSwipeTouchListener
|
|
||||||
import org.kiwix.kiwixmobile.core.main.ServiceWorkerUninitialiser
|
import org.kiwix.kiwixmobile.core.main.ServiceWorkerUninitialiser
|
||||||
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter
|
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter
|
||||||
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.DocumentSection
|
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.DocumentSection
|
||||||
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.TableClickListener
|
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.UNINITIALISER_ADDRESS
|
||||||
import org.kiwix.kiwixmobile.core.main.WebViewCallback
|
import org.kiwix.kiwixmobile.core.main.WebViewCallback
|
||||||
import org.kiwix.kiwixmobile.core.main.WebViewProvider
|
import org.kiwix.kiwixmobile.core.main.WebViewProvider
|
||||||
@ -202,10 +188,10 @@ import java.io.IOException
|
|||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
|
|
||||||
const val SEARCH_ITEM_TITLE_KEY = "searchItemTitle"
|
const val SEARCH_ITEM_TITLE_KEY = "searchItemTitle"
|
||||||
|
const val HIDE_TAB_SWITCHER_DELAY: Long = 300
|
||||||
|
|
||||||
@Suppress("LargeClass")
|
@Suppress("LargeClass")
|
||||||
abstract class CoreReaderFragment :
|
abstract class CoreReaderFragment :
|
||||||
@ -219,15 +205,9 @@ abstract class CoreReaderFragment :
|
|||||||
ShowDonationDialogCallback {
|
ShowDonationDialogCallback {
|
||||||
protected val webViewList = mutableStateListOf<KiwixWebView>()
|
protected val webViewList = mutableStateListOf<KiwixWebView>()
|
||||||
private val webUrlsFlow = MutableStateFlow("")
|
private val webUrlsFlow = MutableStateFlow("")
|
||||||
private var fragmentReaderBinding: FragmentReaderBinding? = null
|
|
||||||
|
|
||||||
var toolbar: Toolbar? = null
|
|
||||||
|
|
||||||
var drawerLayout: DrawerLayout? = null
|
var drawerLayout: DrawerLayout? = null
|
||||||
protected var tableDrawerRightContainer: NavigationView? = null
|
protected var tableDrawerRightContainer: NavigationView? = null
|
||||||
var tabSwitcherRoot: View? = null
|
|
||||||
|
|
||||||
var activityMainRoot: View? = null
|
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@Inject
|
@Inject
|
||||||
@ -262,10 +242,6 @@ abstract class CoreReaderFragment :
|
|||||||
var painter: DarkModeViewPainter? = null
|
var painter: DarkModeViewPainter? = null
|
||||||
protected var currentWebViewIndex by mutableStateOf(0)
|
protected var currentWebViewIndex by mutableStateOf(0)
|
||||||
private var currentTtsWebViewIndex = 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 isFirstTimeMainPageLoaded = true
|
||||||
private var isFromManageExternalLaunch = false
|
private var isFromManageExternalLaunch = false
|
||||||
private val savingTabsMutex = Mutex()
|
private val savingTabsMutex = Mutex()
|
||||||
@ -295,7 +271,6 @@ abstract class CoreReaderFragment :
|
|||||||
private var documentParser: DocumentParser? = null
|
private var documentParser: DocumentParser? = null
|
||||||
private var tts: KiwixTextToSpeech? = null
|
private var tts: KiwixTextToSpeech? = null
|
||||||
private var compatCallback: CompatFindActionModeCallback? = null
|
private var compatCallback: CompatFindActionModeCallback? = null
|
||||||
private var tabsAdapter: TabsAdapter? = null
|
|
||||||
private var zimReaderSource: ZimReaderSource? = null
|
private var zimReaderSource: ZimReaderSource? = null
|
||||||
private var actionMode: ActionMode? = null
|
private var actionMode: ActionMode? = null
|
||||||
private var tempWebViewForUndo: KiwixWebView? = null
|
private var tempWebViewForUndo: KiwixWebView? = null
|
||||||
@ -304,7 +279,6 @@ abstract class CoreReaderFragment :
|
|||||||
private var isFirstRun = false
|
private var isFirstRun = false
|
||||||
private var tableDrawerAdapter: TableDrawerAdapter? = null
|
private var tableDrawerAdapter: TableDrawerAdapter? = null
|
||||||
private var tableDrawerRight: RecyclerView? = null
|
private var tableDrawerRight: RecyclerView? = null
|
||||||
private var tabCallback: ItemTouchHelper.Callback? = null
|
|
||||||
private var donationLayout: FrameLayout? = null
|
private var donationLayout: FrameLayout? = null
|
||||||
private var bookmarkingJob: Job? = null
|
private var bookmarkingJob: Job? = null
|
||||||
private var isBookmarked = false
|
private var isBookmarked = false
|
||||||
@ -517,53 +491,13 @@ abstract class CoreReaderFragment :
|
|||||||
activity?.let {
|
activity?.let {
|
||||||
WebView(it).destroy() // Workaround for buggy webViews see #710
|
WebView(it).destroy() // Workaround for buggy webViews see #710
|
||||||
}
|
}
|
||||||
prepareViews()
|
|
||||||
handleLocaleCheck()
|
handleLocaleCheck()
|
||||||
activity?.setSupportActionBar(toolbar)
|
|
||||||
actionBar = activity?.supportActionBar
|
|
||||||
initHideBackToTopTimer()
|
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()
|
loadDrawerViews()
|
||||||
tableDrawerRight =
|
tableDrawerRight =
|
||||||
tableDrawerRightContainer?.getHeaderView(0)?.findViewById(R.id.right_drawer_list)
|
tableDrawerRightContainer?.getHeaderView(0)?.findViewById(R.id.right_drawer_list)
|
||||||
addFileReader()
|
addFileReader()
|
||||||
setupTabsAdapter()
|
|
||||||
setTableDrawerInfo()
|
setTableDrawerInfo()
|
||||||
setTabListener()
|
|
||||||
activity?.let {
|
activity?.let {
|
||||||
compatCallback = CompatFindActionModeCallback(it)
|
compatCallback = CompatFindActionModeCallback(it)
|
||||||
}
|
}
|
||||||
@ -572,13 +506,6 @@ abstract class CoreReaderFragment :
|
|||||||
loadPrefs()
|
loadPrefs()
|
||||||
updateTitle()
|
updateTitle()
|
||||||
handleIntentExtras(requireActivity().intent)
|
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
|
// Only check intent on first start of activity. Otherwise the intents will enter infinite loops
|
||||||
// when "Don't keep activities" is on.
|
// when "Don't keep activities" is on.
|
||||||
if (savedInstanceState == null) {
|
if (savedInstanceState == null) {
|
||||||
@ -700,50 +627,6 @@ abstract class CoreReaderFragment :
|
|||||||
unsupportedMimeTypeHandler?.setAlertDialogShower(alertDialogShower as AlertDialogShower)
|
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")
|
@Suppress("MagicNumber")
|
||||||
private fun initHideBackToTopTimer() {
|
private fun initHideBackToTopTimer() {
|
||||||
hideBackToTopTimer = object : CountDownTimer(1200, 1200) {
|
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() {
|
private fun setTableDrawerInfo() {
|
||||||
tableDrawerRight?.apply {
|
tableDrawerRight?.apply {
|
||||||
layoutManager = LinearLayoutManager(requireActivity())
|
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() {
|
private fun addFileReader() {
|
||||||
documentParserJs = requireActivity().readFile("js/documentParser.js")
|
documentParserJs = requireActivity().readFile("js/documentParser.js")
|
||||||
documentSections?.clear()
|
documentSections?.clear()
|
||||||
@ -901,7 +752,6 @@ abstract class CoreReaderFragment :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
showSearchPlaceHolderInToolbar(true)
|
showSearchPlaceHolderInToolbar(true)
|
||||||
startAnimation(tabSwitcherRoot, R.anim.slide_down)
|
|
||||||
readerMenuState?.showTabSwitcherOptions()
|
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.
|
* @param shouldCloseZimBook A flag to indicate whether the ZIM book should be closed.
|
||||||
* - Default is `true`, which ensures normal behavior for most scenarios.
|
* - Default is `true`, which ensures normal behavior for most scenarios.
|
||||||
@ -1074,7 +917,7 @@ abstract class CoreReaderFragment :
|
|||||||
@Suppress("ReturnCount", "NestedBlockDepth")
|
@Suppress("ReturnCount", "NestedBlockDepth")
|
||||||
override fun onBackPressed(activity: AppCompatActivity): FragmentActivityExtensions.Super {
|
override fun onBackPressed(activity: AppCompatActivity): FragmentActivityExtensions.Super {
|
||||||
when {
|
when {
|
||||||
tabSwitcherRoot?.visibility == VISIBLE -> {
|
readerScreenState.value.showTabSwitcher -> {
|
||||||
selectTab(
|
selectTab(
|
||||||
if (currentWebViewIndex < webViewList.size) {
|
if (currentWebViewIndex < webViewList.size) {
|
||||||
currentWebViewIndex
|
currentWebViewIndex
|
||||||
@ -1300,16 +1143,11 @@ abstract class CoreReaderFragment :
|
|||||||
}
|
}
|
||||||
safelyCancelBookmarkJob()
|
safelyCancelBookmarkJob()
|
||||||
unBindViewsAndBinding()
|
unBindViewsAndBinding()
|
||||||
tabCallback = null
|
|
||||||
hideBackToTopTimer?.cancel()
|
hideBackToTopTimer?.cancel()
|
||||||
hideBackToTopTimer = null
|
hideBackToTopTimer = null
|
||||||
stopOngoingLoadingAndClearWebViewList()
|
stopOngoingLoadingAndClearWebViewList()
|
||||||
actionBar = null
|
|
||||||
mainMenu = null
|
|
||||||
tabRecyclerView?.adapter = null
|
|
||||||
tableDrawerRight?.adapter = null
|
tableDrawerRight?.adapter = null
|
||||||
tableDrawerAdapter = null
|
tableDrawerAdapter = null
|
||||||
tabsAdapter = null
|
|
||||||
tempWebViewListForUndo.clear()
|
tempWebViewListForUndo.clear()
|
||||||
// create a base Activity class that class this.
|
// create a base Activity class that class this.
|
||||||
deleteCachedFiles(requireActivity())
|
deleteCachedFiles(requireActivity())
|
||||||
@ -1336,17 +1174,10 @@ abstract class CoreReaderFragment :
|
|||||||
|
|
||||||
@SuppressLint("ClickableViewAccessibility")
|
@SuppressLint("ClickableViewAccessibility")
|
||||||
private fun unBindViewsAndBinding() {
|
private fun unBindViewsAndBinding() {
|
||||||
activityMainRoot = null
|
|
||||||
tabRecyclerView = null
|
|
||||||
tabSwitcherRoot = null
|
|
||||||
compatCallback?.finish()
|
compatCallback?.finish()
|
||||||
compatCallback = null
|
compatCallback = null
|
||||||
toolbar?.setOnTouchListener(null)
|
|
||||||
toolbar = null
|
|
||||||
drawerLayout = null
|
drawerLayout = null
|
||||||
tableDrawerRightContainer = null
|
tableDrawerRightContainer = null
|
||||||
fragmentReaderBinding?.root?.removeAllViews()
|
|
||||||
fragmentReaderBinding = null
|
|
||||||
donationLayout?.removeAllViews()
|
donationLayout?.removeAllViews()
|
||||||
donationLayout = null
|
donationLayout = null
|
||||||
}
|
}
|
||||||
@ -1522,18 +1353,7 @@ abstract class CoreReaderFragment :
|
|||||||
reopenBook()
|
reopenBook()
|
||||||
}
|
}
|
||||||
tempWebViewForUndo?.let {
|
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)
|
webViewList.add(index, it)
|
||||||
tabsAdapter?.notifyDataSetChanged()
|
|
||||||
readerScreenState.value.snackBarHostState.snack(
|
readerScreenState.value.snackBarHostState.snack(
|
||||||
context?.getString(R.string.tab_restored).orEmpty(),
|
context?.getString(R.string.tab_restored).orEmpty(),
|
||||||
lifecycleScope = lifecycleScope
|
lifecycleScope = lifecycleScope
|
||||||
@ -1555,7 +1375,6 @@ abstract class CoreReaderFragment :
|
|||||||
currentWebViewIndex = position
|
currentWebViewIndex = position
|
||||||
val webView = safelyGetWebView(position) ?: return
|
val webView = safelyGetWebView(position) ?: return
|
||||||
safelyAddWebView(webView)
|
safelyAddWebView(webView)
|
||||||
tabsAdapter?.selected = currentWebViewIndex
|
|
||||||
updateBottomToolbarVisibility()
|
updateBottomToolbarVisibility()
|
||||||
loadPrefs()
|
loadPrefs()
|
||||||
updateUrlFlow()
|
updateUrlFlow()
|
||||||
@ -1999,7 +1818,6 @@ abstract class CoreReaderFragment :
|
|||||||
private fun restoreDeletedTabs() {
|
private fun restoreDeletedTabs() {
|
||||||
if (tempWebViewListForUndo.isNotEmpty()) {
|
if (tempWebViewListForUndo.isNotEmpty()) {
|
||||||
webViewList.addAll(tempWebViewListForUndo)
|
webViewList.addAll(tempWebViewListForUndo)
|
||||||
tabsAdapter?.notifyDataSetChanged()
|
|
||||||
readerScreenState.value.snackBarHostState.snack(
|
readerScreenState.value.snackBarHostState.snack(
|
||||||
context?.getString(R.string.tabs_restored).orEmpty(),
|
context?.getString(R.string.tabs_restored).orEmpty(),
|
||||||
lifecycleScope = lifecycleScope
|
lifecycleScope = lifecycleScope
|
||||||
@ -2397,10 +2215,8 @@ abstract class CoreReaderFragment :
|
|||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
super.onConfigurationChanged(newConfig)
|
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.
|
// force redraw of donation layout if it is showing.
|
||||||
if (donationLayout?.isVisible == true) {
|
if (readerScreenState.value.shouldShowDonationPopup) {
|
||||||
showDonationLayout()
|
showDonationLayout()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2647,7 +2463,6 @@ abstract class CoreReaderFragment :
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
updateTableOfContents()
|
updateTableOfContents()
|
||||||
tabsAdapter?.notifyDataSetChanged()
|
|
||||||
updateBottomToolbarArrowsAlpha()
|
updateBottomToolbarArrowsAlpha()
|
||||||
val zimFileReader = zimReaderContainer?.zimFileReader
|
val zimFileReader = zimReaderContainer?.zimFileReader
|
||||||
if (hasValidFileAndUrl(getCurrentWebView()?.url, zimFileReader)) {
|
if (hasValidFileAndUrl(getCurrentWebView()?.url, zimFileReader)) {
|
||||||
@ -2681,13 +2496,19 @@ abstract class CoreReaderFragment :
|
|||||||
private fun hasValidFileAndUrl(url: String?, zimFileReader: ZimFileReader?): Boolean =
|
private fun hasValidFileAndUrl(url: String?, zimFileReader: ZimFileReader?): Boolean =
|
||||||
url != null && zimFileReader != null
|
url != null && zimFileReader != null
|
||||||
|
|
||||||
override fun webViewFailedLoading(url: String) {
|
override fun webViewFailedLoading(failingUrl: String) {
|
||||||
if (isAdded) {
|
if (isAdded) {
|
||||||
// If a URL fails to load, update the bookmark toggle.
|
// If a URL fails to load, update the bookmark toggle.
|
||||||
// This fixes the scenario where the previous page is bookmarked and the next
|
// This fixes the scenario where the previous page is bookmarked and the next
|
||||||
// page fails to load, ensuring the bookmark toggle is unset correctly.
|
// page fails to load, ensuring the bookmark toggle is unset correctly.
|
||||||
updateUrlFlow()
|
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) {
|
override fun webViewTitleUpdated(title: String) {
|
||||||
updateTabIcon(webViewList.size)
|
updateTabIcon(webViewList.size)
|
||||||
tabsAdapter?.notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
|
@ -89,7 +89,6 @@ import androidx.compose.ui.draw.alpha
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.platform.LocalWindowInfo
|
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.colorResource
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
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.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
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
|
import org.kiwix.kiwixmobile.core.utils.StyleUtils.fromHtml
|
||||||
|
|
||||||
const val CONTENT_LOADING_PROGRESSBAR_TESTING_TAG = "contentLoadingProgressBarTestingTag"
|
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)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Suppress("ComposableLambdaParameterNaming")
|
@Suppress("ComposableLambdaParameterNaming")
|
||||||
@ -157,10 +162,10 @@ fun ReaderScreen(
|
|||||||
navigationIcon: @Composable () -> Unit
|
navigationIcon: @Composable () -> Unit
|
||||||
) {
|
) {
|
||||||
val bottomNavHeightInDp = with(LocalDensity.current) { state.bottomNavigationHeight.toDp() }
|
val bottomNavHeightInDp = with(LocalDensity.current) { state.bottomNavigationHeight.toDp() }
|
||||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||||
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
||||||
LaunchedEffect(bottomAppBarScrollBehavior.state.heightOffset) {
|
LaunchedEffect(bottomAppBarScrollBehavior.state.heightOffset) {
|
||||||
onBottomScrollOffsetChanged(scrollBehavior.state.heightOffset)
|
onBottomScrollOffsetChanged(bottomAppBarScrollBehavior.state.heightOffset)
|
||||||
}
|
}
|
||||||
KiwixDialogTheme {
|
KiwixDialogTheme {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
@ -169,21 +174,21 @@ fun ReaderScreen(
|
|||||||
ReaderTopBar(
|
ReaderTopBar(
|
||||||
state,
|
state,
|
||||||
actionMenuItems,
|
actionMenuItems,
|
||||||
scrollBehavior,
|
topAppBarScrollBehavior,
|
||||||
navigationIcon
|
navigationIcon
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
floatingActionButton = { BackToTopFab(state) },
|
floatingActionButton = { BackToTopFab(state) },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.systemBarsPadding()
|
.systemBarsPadding()
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
|
||||||
.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection)
|
|
||||||
.padding(bottom = bottomNavHeightInDp)
|
.padding(bottom = bottomNavHeightInDp)
|
||||||
|
.semantics { testTag = READER_SCREEN_TESTING_TAG }
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
ReaderContentLayout(
|
ReaderContentLayout(
|
||||||
state,
|
state,
|
||||||
Modifier.padding(paddingValues),
|
Modifier.padding(paddingValues),
|
||||||
bottomAppBarScrollBehavior
|
bottomAppBarScrollBehavior,
|
||||||
|
topAppBarScrollBehavior
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,7 +200,7 @@ fun ReaderScreen(
|
|||||||
private fun ReaderTopBar(
|
private fun ReaderTopBar(
|
||||||
state: ReaderScreenState,
|
state: ReaderScreenState,
|
||||||
actionMenuItems: List<ActionMenuItem>,
|
actionMenuItems: List<ActionMenuItem>,
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
topAppBarScrollBehavior: TopAppBarScrollBehavior,
|
||||||
navigationIcon: @Composable () -> Unit,
|
navigationIcon: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
if (!state.shouldShowFullScreenMode && !state.fullScreenItem.first) {
|
if (!state.shouldShowFullScreenMode && !state.fullScreenItem.first) {
|
||||||
@ -203,7 +208,7 @@ private fun ReaderTopBar(
|
|||||||
title = if (state.showTabSwitcher) "" else state.readerScreenTitle,
|
title = if (state.showTabSwitcher) "" else state.readerScreenTitle,
|
||||||
navigationIcon = navigationIcon,
|
navigationIcon = navigationIcon,
|
||||||
actionMenuItems = actionMenuItems,
|
actionMenuItems = actionMenuItems,
|
||||||
topAppBarScrollBehavior = scrollBehavior,
|
topAppBarScrollBehavior = topAppBarScrollBehavior,
|
||||||
searchBar =
|
searchBar =
|
||||||
searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps)
|
searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps)
|
||||||
)
|
)
|
||||||
@ -215,7 +220,8 @@ private fun ReaderTopBar(
|
|||||||
private fun ReaderContentLayout(
|
private fun ReaderContentLayout(
|
||||||
state: ReaderScreenState,
|
state: ReaderScreenState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior
|
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior,
|
||||||
|
topAppBarScrollBehavior: TopAppBarScrollBehavior
|
||||||
) {
|
) {
|
||||||
Box(modifier = modifier.fillMaxSize()) {
|
Box(modifier = modifier.fillMaxSize()) {
|
||||||
TabSwitcherAnimated(state)
|
TabSwitcherAnimated(state)
|
||||||
@ -225,7 +231,7 @@ private fun ReaderContentLayout(
|
|||||||
state.fullScreenItem.first -> ShowFullScreenView(state)
|
state.fullScreenItem.first -> ShowFullScreenView(state)
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
ShowZIMFileContent(state)
|
ShowZIMFileContent(state, bottomAppBarScrollBehavior, topAppBarScrollBehavior)
|
||||||
ShowProgressBarIfZIMFilePageIsLoading(state)
|
ShowProgressBarIfZIMFilePageIsLoading(state)
|
||||||
Column(Modifier.align(Alignment.BottomCenter)) {
|
Column(Modifier.align(Alignment.BottomCenter)) {
|
||||||
TtsControls(state)
|
TtsControls(state)
|
||||||
@ -255,11 +261,11 @@ private fun TabSwitcherAnimated(state: ReaderScreenState) {
|
|||||||
val transitionSpec = remember {
|
val transitionSpec = remember {
|
||||||
slideInVertically(
|
slideInVertically(
|
||||||
initialOffsetY = { -it },
|
initialOffsetY = { -it },
|
||||||
animationSpec = tween(durationMillis = 300)
|
animationSpec = tween(durationMillis = HIDE_TAB_SWITCHER_DELAY.toInt())
|
||||||
) + fadeIn() togetherWith
|
) + fadeIn() togetherWith
|
||||||
slideOutVertically(
|
slideOutVertically(
|
||||||
targetOffsetY = { -it },
|
targetOffsetY = { -it },
|
||||||
animationSpec = tween(durationMillis = 300)
|
animationSpec = tween(durationMillis = HIDE_TAB_SWITCHER_DELAY.toInt())
|
||||||
) + fadeOut()
|
) + fadeOut()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +273,9 @@ private fun TabSwitcherAnimated(state: ReaderScreenState) {
|
|||||||
visible = state.showTabSwitcher,
|
visible = state.showTabSwitcher,
|
||||||
enter = transitionSpec.targetContentEnter,
|
enter = transitionSpec.targetContentEnter,
|
||||||
exit = transitionSpec.initialContentExit,
|
exit = transitionSpec.initialContentExit,
|
||||||
modifier = Modifier.zIndex(1f),
|
modifier = Modifier
|
||||||
|
.zIndex(1f)
|
||||||
|
.semantics { testTag = TAB_SWITCHER_VIEW_TESTING_TAG },
|
||||||
) {
|
) {
|
||||||
TabSwitcherView(
|
TabSwitcherView(
|
||||||
state.kiwixWebViewList,
|
state.kiwixWebViewList,
|
||||||
@ -349,8 +357,13 @@ private fun BoxScope.CloseFullScreenImageButton(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ShowZIMFileContent(state: ReaderScreenState) {
|
private fun ShowZIMFileContent(
|
||||||
|
state: ReaderScreenState,
|
||||||
|
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior,
|
||||||
|
topAppBarScrollBehavior: TopAppBarScrollBehavior
|
||||||
|
) {
|
||||||
state.selectedWebView?.let { selectedWebView ->
|
state.selectedWebView?.let { selectedWebView ->
|
||||||
key(selectedWebView) {
|
key(selectedWebView) {
|
||||||
AndroidView(
|
AndroidView(
|
||||||
@ -359,14 +372,23 @@ private fun ShowZIMFileContent(state: ReaderScreenState) {
|
|||||||
FrameLayout(context).apply {
|
FrameLayout(context).apply {
|
||||||
// Ensure the WebView has no parent before adding
|
// Ensure the WebView has no parent before adding
|
||||||
(selectedWebView.parent as? ViewGroup)?.removeView(selectedWebView)
|
(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)
|
addView(selectedWebView)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize()
|
||||||
.fillMaxSize()
|
|
||||||
.verticalScroll(rememberScrollState())
|
|
||||||
)
|
)
|
||||||
// TODO handle the unnecessary vertical scroll when webView page chnaged.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,6 +679,7 @@ private fun BoxScope.CloseAllTabButton(onCloseAllTabs: () -> Unit) {
|
|||||||
.graphicsLayer {
|
.graphicsLayer {
|
||||||
rotationZ = rotation
|
rotationZ = rotation
|
||||||
}
|
}
|
||||||
|
.semantics { testTag = CLOSE_ALL_TABS_BUTTON_TESTING_TAG }
|
||||||
.clickable(
|
.clickable(
|
||||||
enabled = !isAnimating,
|
enabled = !isAnimating,
|
||||||
onClick = {
|
onClick = {
|
||||||
@ -732,7 +755,8 @@ private fun TabItemHeader(
|
|||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(end = FOUR_DP)
|
.padding(end = FOUR_DP)
|
||||||
.weight(1f),
|
.weight(1f)
|
||||||
|
.semantics { testTag = TAB_TITLE_TESTING_TAG },
|
||||||
style = MaterialTheme.typography.labelSmall
|
style = MaterialTheme.typography.labelSmall
|
||||||
)
|
)
|
||||||
IconButton(
|
IconButton(
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Kiwix Android
|
|
||||||
~ Copyright (c) 2020 Kiwix <android.kiwix.org>
|
|
||||||
~ 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 <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/navigation_fragment_main_drawer_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<include layout="@layout/reader_fragment_content" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/no_open_book_text"
|
|
||||||
style="@style/no_content"
|
|
||||||
android:text="@string/no_open_book"
|
|
||||||
android:layout_marginTop="@dimen/material_design_appbar_size"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="0.45" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/go_to_library_button_no_open_book"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:text="@string/open_library"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintEnd_toEndOf="@+id/no_open_book_text"
|
|
||||||
app:layout_constraintStart_toStartOf="@+id/no_open_book_text"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/no_open_book_text"/>
|
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/fullscreen_video_container"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:background="@android:color/black"
|
|
||||||
android:visibility="invisible" />
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/donation_layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
|
@ -1,31 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="?attr/actionBarItemBackground"
|
|
||||||
android:minHeight="@dimen/material_minimum_height_and_width"
|
|
||||||
android:minWidth="@dimen/material_minimum_height_and_width"
|
|
||||||
android:paddingStart="12dp"
|
|
||||||
android:paddingEnd="12dp"
|
|
||||||
tools:ignore="Overdraw">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/ic_tab_switcher_text"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:background="@drawable/border_tab_switcher"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textColor="@android:color/white"
|
|
||||||
android:textSize="12sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:minHeight="20dp"
|
|
||||||
android:minWidth="20dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="12"
|
|
||||||
tools:textColor="@android:color/black" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Kiwix Android
|
|
||||||
~ Copyright (c) 2020 Kiwix <android.kiwix.org>
|
|
||||||
~ 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 <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways"
|
|
||||||
app:popupTheme="@style/KiwixTheme"
|
|
||||||
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
|
||||||
|
|
||||||
</merge>
|
|
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/app_bar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
|
||||||
|
|
||||||
<include layout="@layout/layout_toolbar" />
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
@ -1,33 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<merge xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:popupTheme="@style/KiwixTheme"
|
|
||||||
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:id="@+id/toolbarWithSearchPlaceholder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:background="@drawable/search_placeholder_background"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:hint="@string/search_label"
|
|
||||||
android:padding="5dip"
|
|
||||||
android:layout_marginStart="5dip"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:drawableRightCompat="@drawable/action_search"
|
|
||||||
android:drawablePadding="10dip" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
</androidx.appcompat.widget.Toolbar>
|
|
||||||
</merge>
|
|
@ -1,145 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?><!--
|
|
||||||
~ Kiwix Android
|
|
||||||
~ Copyright (c) 2020 Kiwix <android.kiwix.org>
|
|
||||||
~ 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 <http://www.gnu.org/licenses/>.
|
|
||||||
~
|
|
||||||
-->
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/activity_main_root"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:animateLayoutChanges="true">
|
|
||||||
|
|
||||||
<org.kiwix.kiwixmobile.core.utils.NestedCoordinatorLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/activity_main_content_frame"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
|
|
||||||
<include
|
|
||||||
android:id="@+id/activity_main_tab_switcher"
|
|
||||||
layout="@layout/tab_switcher"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:id="@+id/fragment_main_app_bar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<include layout="@layout/layout_toolbar" />
|
|
||||||
|
|
||||||
<androidx.core.widget.ContentLoadingProgressBar
|
|
||||||
android:id="@+id/main_fragment_progress_view"
|
|
||||||
style="?android:attr/progressBarStyleHorizontal"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="@dimen/progress_view_height"
|
|
||||||
android:indeterminate="false"
|
|
||||||
android:max="100"
|
|
||||||
android:theme="@style/ThemeOverlay.KiwixTheme.ProgressBar"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/toolbar"
|
|
||||||
tools:progress="70" />
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="top"
|
|
||||||
app:layout_anchor="@id/bottom_toolbar">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/activity_main_button_pause_tts"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:alpha="0.6"
|
|
||||||
android:text="@string/tts_pause"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/activity_main_button_stop_tts"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/activity_main_button_stop_tts"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:alpha="0.6"
|
|
||||||
android:text="@string/stop"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.5"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/activity_main_button_pause_tts"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.Group
|
|
||||||
android:id="@+id/activity_main_tts_controls"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:constraint_referenced_ids="activity_main_button_pause_tts,activity_main_button_stop_tts"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
||||||
|
|
||||||
|
|
||||||
<include layout="@layout/bottom_toolbar" />
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/activity_main_back_to_top_fab"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:src="@drawable/action_find_previous"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:fabSize="mini"
|
|
||||||
android:contentDescription="@string/pref_back_to_top"
|
|
||||||
app:layout_anchor="@id/bottom_toolbar"
|
|
||||||
app:layout_dodgeInsetEdges="bottom"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
|
|
||||||
|
|
||||||
</org.kiwix.kiwixmobile.core.utils.NestedCoordinatorLayout>
|
|
||||||
|
|
||||||
<ImageButton
|
|
||||||
android:id="@+id/activity_main_fullscreen_button"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_margin="@dimen/fullscreen_control_button_margin"
|
|
||||||
android:background="?colorOnSurface"
|
|
||||||
android:contentDescription="@string/menu_exit_full_screen"
|
|
||||||
android:src="@drawable/fullscreen_exit"
|
|
||||||
app:tint="?colorSurface"
|
|
||||||
android:visibility="gone"
|
|
||||||
android:minWidth="@dimen/material_minimum_height_and_width"
|
|
||||||
android:minHeight="@dimen/material_minimum_height_and_width"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
|
||||||
android:id="@+id/snackbar_root"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/tab_switcher_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
|
||||||
android:id="@+id/tab_switcher_close_all_tabs"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginBottom="24dp"
|
|
||||||
android:contentDescription="@string/close_all_tabs"
|
|
||||||
android:src="@drawable/ic_close_black_24dp"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -1,50 +0,0 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_search"
|
|
||||||
android:icon="@drawable/action_search"
|
|
||||||
android:title="@string/search_label"
|
|
||||||
android:visible="false"
|
|
||||||
app:showAsAction="always"
|
|
||||||
tools:visible="true"
|
|
||||||
tools:ignore="AlwaysShowAction" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_tab_switcher"
|
|
||||||
android:title="@string/switch_tabs"
|
|
||||||
app:actionLayout="@layout/ic_tab_switcher"
|
|
||||||
app:showAsAction="always" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_add_note"
|
|
||||||
android:icon="@drawable/ic_add_note"
|
|
||||||
android:title="@string/take_notes"
|
|
||||||
android:visible="false"
|
|
||||||
app:showAsAction="never" />
|
|
||||||
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_random_article"
|
|
||||||
android:icon="@drawable/action_randomarticle"
|
|
||||||
android:title="@string/menu_random_article"
|
|
||||||
android:visible="false"
|
|
||||||
app:showAsAction="never"
|
|
||||||
tools:visible="true" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_fullscreen"
|
|
||||||
android:title="@string/menu_full_screen"
|
|
||||||
android:visible="false"
|
|
||||||
app:showAsAction="never"
|
|
||||||
tools:visible="true" />
|
|
||||||
|
|
||||||
<item
|
|
||||||
android:id="@+id/menu_read_aloud"
|
|
||||||
android:title="@string/menu_read_aloud"
|
|
||||||
android:visible="false"
|
|
||||||
app:showAsAction="never"
|
|
||||||
tools:visible="true" />
|
|
||||||
|
|
||||||
</menu>
|
|
@ -13,14 +13,16 @@
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/find_prev"
|
android:id="@+id/find_prev"
|
||||||
android:icon="@drawable/action_find_previous"
|
android:icon="@drawable/action_find_previous"
|
||||||
android:title="@string/previous"
|
android:title="@string/previous"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always"
|
||||||
|
tools:ignore="AlwaysShowAction" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/find_next"
|
android:id="@+id/find_next"
|
||||||
android:icon="@drawable/action_find_next"
|
android:icon="@drawable/action_find_next"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user