mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 02:36:24 -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() {
|
||||
topLevel {
|
||||
clickReaderOnBottomNav {
|
||||
assertReaderScreenDisplayed()
|
||||
assertReaderScreenDisplayed(composeTestRule)
|
||||
}
|
||||
clickDownloadOnBottomNav {
|
||||
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.refresh
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
|
||||
import org.kiwix.kiwixmobile.ui.BOOK_ITEM_TESTING_TAG
|
||||
|
||||
fun library(func: LibraryRobot.() -> Unit) = LibraryRobot().applyWithViewHierarchyPrinting(func)
|
||||
@ -102,8 +103,8 @@ class LibraryRobot : BaseRobot() {
|
||||
}
|
||||
|
||||
fun waitUntilZimFilesRefreshing(composeTestRule: ComposeContentTestRule) {
|
||||
pauseForBetterTestPerformance()
|
||||
testFlakyView({
|
||||
composeTestRule.waitUntilTimeout()
|
||||
composeTestRule.onNodeWithTag(CONTENT_LOADING_PROGRESSBAR_TESTING_TAG)
|
||||
.assertIsNotDisplayed()
|
||||
})
|
||||
|
@ -18,15 +18,20 @@
|
||||
|
||||
package org.kiwix.kiwixmobile.nav.destination.reader
|
||||
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import applyWithViewHierarchyPrinting
|
||||
import org.kiwix.kiwixmobile.BaseRobot
|
||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
import org.kiwix.kiwixmobile.core.main.reader.READER_SCREEN_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
|
||||
|
||||
fun reader(func: ReaderRobot.() -> Unit) = ReaderRobot().applyWithViewHierarchyPrinting(func)
|
||||
|
||||
class ReaderRobot : BaseRobot() {
|
||||
fun assertReaderScreenDisplayed() {
|
||||
isVisible(ViewId(R.id.activity_main_root))
|
||||
fun assertReaderScreenDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||
composeTestRule.apply {
|
||||
waitUntilTimeout()
|
||||
onNodeWithTag(READER_SCREEN_TESTING_TAG).assertExists()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,13 @@ package org.kiwix.kiwixmobile.page.history
|
||||
import android.util.Log
|
||||
import androidx.compose.ui.test.assertTextEquals
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import androidx.compose.ui.test.longClick
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.action.ViewActions.longClick
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.web.sugar.Web.onWebView
|
||||
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
|
||||
@ -36,11 +38,13 @@ import junit.framework.AssertionFailedError
|
||||
import org.kiwix.kiwixmobile.BaseRobot
|
||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
import org.kiwix.kiwixmobile.core.main.reader.TAB_SWITCHER_VIEW_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.core.page.DELETE_MENU_ICON_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.core.ui.components.TOOLBAR_TITLE_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.core.utils.dialog.ALERT_DIALOG_TITLE_TEXT_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
|
||||
|
||||
fun navigationHistory(func: NavigationHistoryRobot.() -> Unit) =
|
||||
NavigationHistoryRobot().applyWithViewHierarchyPrinting(func)
|
||||
@ -55,12 +59,14 @@ class NavigationHistoryRobot : BaseRobot() {
|
||||
isVisible(ViewId(readerFragment))
|
||||
}
|
||||
|
||||
fun closeTabSwitcherIfVisible() {
|
||||
fun closeTabSwitcherIfVisible(composeTestRule: ComposeContentTestRule) {
|
||||
try {
|
||||
pauseForBetterTestPerformance()
|
||||
isVisible(ViewId(R.id.tab_switcher_close_all_tabs))
|
||||
pressBack()
|
||||
} catch (_: Exception) {
|
||||
composeTestRule.apply {
|
||||
waitUntilTimeout()
|
||||
onNodeWithTag(TAB_SWITCHER_VIEW_TESTING_TAG).assertExists()
|
||||
pressBack()
|
||||
}
|
||||
} catch (_: AssertionError) {
|
||||
Log.i(
|
||||
"NAVIGATION_HISTORY_TEST",
|
||||
"Couldn't found tab switcher, probably it is not visible"
|
||||
@ -91,14 +97,24 @@ class NavigationHistoryRobot : BaseRobot() {
|
||||
)
|
||||
}
|
||||
|
||||
fun longClickOnBackwardButton() {
|
||||
pauseForBetterTestPerformance()
|
||||
testFlakyView({ onView(withId(R.id.bottom_toolbar_arrow_back)).perform(longClick()) })
|
||||
fun longClickOnBackwardButton(composeTestRule: ComposeContentTestRule) {
|
||||
composeTestRule.apply {
|
||||
waitUntilTimeout()
|
||||
testFlakyView({
|
||||
onNodeWithContentDescription(context.getString(R.string.go_to_previous_page))
|
||||
.performTouchInput { longClick() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun longClickOnForwardButton() {
|
||||
pauseForBetterTestPerformance()
|
||||
longClickOn(ViewId(R.id.bottom_toolbar_arrow_forward))
|
||||
fun longClickOnForwardButton(composeTestRule: ComposeContentTestRule) {
|
||||
composeTestRule.apply {
|
||||
waitUntilTimeout()
|
||||
testFlakyView({
|
||||
onNodeWithContentDescription(context.getString(R.string.go_to_next_page))
|
||||
.performTouchInput { longClick() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun assertBackwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||
@ -117,9 +133,12 @@ class NavigationHistoryRobot : BaseRobot() {
|
||||
}
|
||||
}
|
||||
|
||||
fun clickOnBackwardButton() {
|
||||
pauseForBetterTestPerformance()
|
||||
clickOn(ViewId(R.id.bottom_toolbar_arrow_back))
|
||||
fun clickOnBackwardButton(composeTestRule: ComposeContentTestRule) {
|
||||
composeTestRule.apply {
|
||||
waitUntilTimeout()
|
||||
onNodeWithContentDescription(context.getString(R.string.go_to_previous_page))
|
||||
.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
fun assertForwardNavigationHistoryDialogDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||
|
@ -147,14 +147,14 @@ class NavigationHistoryTest : BaseActivityTest() {
|
||||
}
|
||||
StandardActions.closeDrawer() // close the drawer if open before running the test cases.
|
||||
navigationHistory {
|
||||
closeTabSwitcherIfVisible()
|
||||
closeTabSwitcherIfVisible(composeTestRule)
|
||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||
clickOnAndroidArticle()
|
||||
longClickOnBackwardButton()
|
||||
longClickOnBackwardButton(composeTestRule)
|
||||
assertBackwardNavigationHistoryDialogDisplayed(composeTestRule)
|
||||
pressBack()
|
||||
clickOnBackwardButton()
|
||||
longClickOnForwardButton()
|
||||
clickOnBackwardButton(composeTestRule)
|
||||
longClickOnForwardButton(composeTestRule)
|
||||
assertForwardNavigationHistoryDialogDisplayed(composeTestRule)
|
||||
clickOnDeleteHistory(composeTestRule)
|
||||
assertDeleteDialogDisplayed(composeTestRule)
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.kiwix.kiwixmobile.reader
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.Lifecycle
|
||||
@ -46,6 +47,7 @@ import org.kiwix.kiwixmobile.BaseActivityTest
|
||||
import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
|
||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||
import org.kiwix.kiwixmobile.core.utils.TestingUtils.COMPOSE_TEST_RULE_ORDER
|
||||
import org.kiwix.kiwixmobile.core.utils.TestingUtils.RETRY_RULE_ORDER
|
||||
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
||||
import org.kiwix.kiwixmobile.nav.destination.library.local.LocalLibraryFragmentDirections
|
||||
@ -64,6 +66,9 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
|
||||
@JvmField
|
||||
val retryRule = RetryRule()
|
||||
|
||||
@get:Rule(order = COMPOSE_TEST_RULE_ORDER)
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private lateinit var kiwixMainActivity: KiwixMainActivity
|
||||
|
||||
@Before
|
||||
@ -136,10 +141,10 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
|
||||
openKiwixReaderFragmentWithFile(zimFile)
|
||||
reader {
|
||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||
clickOnTabIcon()
|
||||
clickOnClosedAllTabsButton()
|
||||
clickOnUndoButton()
|
||||
assertTabRestored()
|
||||
clickOnTabIcon(composeTestRule)
|
||||
clickOnClosedAllTabsButton(composeTestRule)
|
||||
clickOnUndoButton(composeTestRule)
|
||||
assertTabRestored(composeTestRule)
|
||||
pressBack()
|
||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||
}
|
||||
@ -171,6 +176,8 @@ class KiwixReaderFragmentTest : BaseActivityTest() {
|
||||
openKiwixReaderFragmentWithFile(downloadingZimFile)
|
||||
reader {
|
||||
checkZimFileLoadedSuccessful(R.id.readerFragment)
|
||||
clickOnTabIcon(composeTestRule)
|
||||
clickOnTabIcon(composeTestRule)
|
||||
// test the whole welcome page is loaded or not
|
||||
assertArticleLoaded("Hydrogène")
|
||||
assertArticleLoaded("Automobile")
|
||||
|
@ -18,10 +18,12 @@
|
||||
|
||||
package org.kiwix.kiwixmobile.reader
|
||||
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.compose.ui.test.assertTextEquals
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import androidx.compose.ui.test.onAllNodesWithTag
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.espresso.web.sugar.Web.onWebView
|
||||
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
|
||||
import androidx.test.espresso.web.webdriver.DriverAtoms.webClick
|
||||
@ -29,11 +31,13 @@ import androidx.test.espresso.web.webdriver.Locator
|
||||
import applyWithViewHierarchyPrinting
|
||||
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
|
||||
import org.kiwix.kiwixmobile.BaseRobot
|
||||
import org.kiwix.kiwixmobile.Findable.Text
|
||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
import org.kiwix.kiwixmobile.core.main.reader.CLOSE_ALL_TABS_BUTTON_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.core.main.reader.TAB_MENU_ITEM_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.core.main.reader.TAB_TITLE_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
||||
import org.kiwix.kiwixmobile.testutils.TestUtils.waitUntilTimeout
|
||||
|
||||
fun reader(func: ReaderRobot.() -> Unit) = ReaderRobot().applyWithViewHierarchyPrinting(func)
|
||||
|
||||
@ -45,30 +49,43 @@ class ReaderRobot : BaseRobot() {
|
||||
isVisible(ViewId(readerFragment))
|
||||
}
|
||||
|
||||
fun clickOnTabIcon() {
|
||||
pauseForBetterTestPerformance()
|
||||
testFlakyView({ onView(withId(R.id.ic_tab_switcher_text)).perform(click()) })
|
||||
fun clickOnTabIcon(composeTestRule: ComposeContentTestRule) {
|
||||
composeTestRule.apply {
|
||||
waitUntilTimeout()
|
||||
testFlakyView({
|
||||
onNodeWithTag(TAB_MENU_ITEM_TESTING_TAG).performClick()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun clickOnClosedAllTabsButton() {
|
||||
pauseForBetterTestPerformance()
|
||||
clickOn(ViewId(R.id.tab_switcher_close_all_tabs))
|
||||
fun clickOnClosedAllTabsButton(composeTestRule: ComposeContentTestRule) {
|
||||
composeTestRule.apply {
|
||||
waitUntilTimeout()
|
||||
testFlakyView({
|
||||
onNodeWithTag(CLOSE_ALL_TABS_BUTTON_TESTING_TAG).performClick()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun clickOnUndoButton() {
|
||||
fun clickOnUndoButton(composeTestRule: ComposeContentTestRule) {
|
||||
try {
|
||||
onView(withText("UNDO")).perform(click())
|
||||
} catch (runtimeException: RuntimeException) {
|
||||
composeTestRule.apply {
|
||||
onNodeWithText("UNDO", useUnmergedTree = true)
|
||||
.performClick()
|
||||
}
|
||||
} catch (_: AssertionError) {
|
||||
if (retryCountForClickOnUndoButton > 0) {
|
||||
retryCountForClickOnUndoButton--
|
||||
clickOnUndoButton()
|
||||
clickOnUndoButton(composeTestRule)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun assertTabRestored() {
|
||||
pauseForBetterTestPerformance()
|
||||
isVisible(Text("Test Zim"))
|
||||
fun assertTabRestored(composeTestRule: ComposeContentTestRule) {
|
||||
composeTestRule.apply {
|
||||
waitUntilTimeout()
|
||||
onAllNodesWithTag(TAB_TITLE_TESTING_TAG)[0].assertTextEquals("Test Zim")
|
||||
}
|
||||
}
|
||||
|
||||
private fun pauseForBetterTestPerformance() {
|
||||
|
@ -27,9 +27,6 @@ import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTextClearance
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.web.sugar.Web.onWebView
|
||||
import androidx.test.espresso.web.webdriver.DriverAtoms.findElement
|
||||
import androidx.test.espresso.web.webdriver.Locator
|
||||
@ -38,7 +35,7 @@ import applyWithViewHierarchyPrinting
|
||||
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
|
||||
import org.kiwix.kiwixmobile.BaseRobot
|
||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
import org.kiwix.kiwixmobile.core.page.SEARCH_ICON_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.core.search.SEARCH_FIELD_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.core.search.SEARCH_ITEM_TESTING_TAG
|
||||
import org.kiwix.kiwixmobile.core.ui.components.NAVIGATION_ICON_TESTING_TAG
|
||||
@ -122,12 +119,16 @@ class SearchRobot : BaseRobot() {
|
||||
composeTestRule.onNodeWithTag(NAVIGATION_ICON_TESTING_TAG).performClick()
|
||||
}
|
||||
|
||||
private fun openSearchScreen() {
|
||||
testFlakyView({ onView(withId(R.id.menu_search)).perform(click()) })
|
||||
private fun openSearchScreen(composeTestRule: ComposeContentTestRule) {
|
||||
testFlakyView(
|
||||
{
|
||||
composeTestRule.onNodeWithTag(SEARCH_ICON_TESTING_TAG).performClick()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun searchAndClickOnArticle(searchString: String, composeTestRule: ComposeContentTestRule) {
|
||||
openSearchScreen()
|
||||
openSearchScreen(composeTestRule)
|
||||
searchWithFrequentlyTypedWords(searchString, composeTestRule = composeTestRule)
|
||||
clickOnSearchItemInSearchList(composeTestRule)
|
||||
checkZimFileSearchSuccessful(org.kiwix.kiwixmobile.R.id.readerFragment)
|
||||
|
@ -128,7 +128,7 @@ class GetContentShortcutTest {
|
||||
onlineLibrary { assertOnlineLibraryFragmentDisplayed(composeTestRule) }
|
||||
topLevel {
|
||||
clickReaderOnBottomNav {
|
||||
assertReaderScreenDisplayed()
|
||||
assertReaderScreenDisplayed(composeTestRule)
|
||||
}
|
||||
clickDownloadOnBottomNav {
|
||||
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.main.CoreMainActivity
|
||||
import org.kiwix.kiwixmobile.core.main.reader.CoreReaderFragment
|
||||
import org.kiwix.kiwixmobile.core.main.reader.HIDE_TAB_SWITCHER_DELAY
|
||||
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin
|
||||
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromExternalLaunch
|
||||
import org.kiwix.kiwixmobile.core.main.reader.RestoreOrigin.FromSearchScreen
|
||||
@ -62,8 +63,6 @@ import org.kiwix.kiwixmobile.core.utils.files.FileUtils
|
||||
import org.kiwix.kiwixmobile.core.utils.files.Log
|
||||
import java.io.File
|
||||
|
||||
private const val HIDE_TAB_SWITCHER_DELAY: Long = 300
|
||||
|
||||
class KiwixReaderFragment : CoreReaderFragment() {
|
||||
private var isFullScreenVideo: Boolean = false
|
||||
|
||||
|
@ -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
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.Menu
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import org.kiwix.kiwixmobile.core.data.DataSource
|
||||
import org.kiwix.kiwixmobile.core.di.ActivityScope
|
||||
import org.kiwix.kiwixmobile.core.main.KiwixWebView
|
||||
import org.kiwix.kiwixmobile.core.main.MainMenu
|
||||
import org.kiwix.kiwixmobile.core.main.MainMenu.Factory
|
||||
import org.kiwix.kiwixmobile.core.main.MainMenu.MenuClickListener
|
||||
import org.kiwix.kiwixmobile.core.main.MainRepositoryActions
|
||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
||||
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
||||
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
|
||||
|
||||
@ -44,33 +37,5 @@ abstract class ActivityModule {
|
||||
@ActivityScope
|
||||
fun providesMainPresenter(dataSource: DataSource): MainRepositoryActions =
|
||||
MainRepositoryActions(dataSource)
|
||||
|
||||
@Provides
|
||||
@ActivityScope
|
||||
fun providesMainMenuFactory(
|
||||
activity: Activity,
|
||||
zimReaderContainer: ZimReaderContainer
|
||||
): MainMenu.Factory = object : Factory {
|
||||
override fun create(
|
||||
menu: Menu,
|
||||
webViews: MutableList<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.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Canvas
|
||||
import android.media.AudioManager
|
||||
import android.media.AudioManager.OnAudioFocusChangeListener
|
||||
import android.net.Uri
|
||||
@ -44,24 +43,18 @@ import android.view.Gravity.BOTTOM
|
||||
import android.view.Gravity.CENTER_HORIZONTAL
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.webkit.WebBackForwardList
|
||||
import android.webkit.WebView
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.annotation.AnimRes
|
||||
import androidx.appcompat.app.ActionBar
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Menu
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
@ -83,10 +76,8 @@ import androidx.core.view.isVisible
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
|
||||
import com.google.android.material.bottomappbar.BottomAppBar
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
@ -114,14 +105,12 @@ import org.kiwix.kiwixmobile.core.base.BaseFragment
|
||||
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
|
||||
import org.kiwix.kiwixmobile.core.dao.LibkiwixBookmarks
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.WebViewHistoryEntity
|
||||
import org.kiwix.kiwixmobile.core.databinding.FragmentReaderBinding
|
||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.consumeObservable
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isLandScapeMode
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.observeNavigationResult
|
||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.requestNotificationPermission
|
||||
import org.kiwix.kiwixmobile.core.extensions.ViewGroupExtensions.findFirstTextView
|
||||
import org.kiwix.kiwixmobile.core.extensions.closeFullScreenMode
|
||||
import org.kiwix.kiwixmobile.core.extensions.showFullScreenMode
|
||||
import org.kiwix.kiwixmobile.core.extensions.snack
|
||||
@ -140,14 +129,11 @@ import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech
|
||||
import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech.OnInitSucceedListener
|
||||
import org.kiwix.kiwixmobile.core.main.KiwixTextToSpeech.OnSpeakingListener
|
||||
import org.kiwix.kiwixmobile.core.main.KiwixWebView
|
||||
import org.kiwix.kiwixmobile.core.main.MainMenu
|
||||
import org.kiwix.kiwixmobile.core.main.MainRepositoryActions
|
||||
import org.kiwix.kiwixmobile.core.main.OnSwipeTouchListener
|
||||
import org.kiwix.kiwixmobile.core.main.ServiceWorkerUninitialiser
|
||||
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter
|
||||
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.DocumentSection
|
||||
import org.kiwix.kiwixmobile.core.main.TableDrawerAdapter.TableClickListener
|
||||
import org.kiwix.kiwixmobile.core.main.TabsAdapter
|
||||
import org.kiwix.kiwixmobile.core.main.UNINITIALISER_ADDRESS
|
||||
import org.kiwix.kiwixmobile.core.main.WebViewCallback
|
||||
import org.kiwix.kiwixmobile.core.main.WebViewProvider
|
||||
@ -202,10 +188,10 @@ import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
|
||||
const val SEARCH_ITEM_TITLE_KEY = "searchItemTitle"
|
||||
const val HIDE_TAB_SWITCHER_DELAY: Long = 300
|
||||
|
||||
@Suppress("LargeClass")
|
||||
abstract class CoreReaderFragment :
|
||||
@ -219,15 +205,9 @@ abstract class CoreReaderFragment :
|
||||
ShowDonationDialogCallback {
|
||||
protected val webViewList = mutableStateListOf<KiwixWebView>()
|
||||
private val webUrlsFlow = MutableStateFlow("")
|
||||
private var fragmentReaderBinding: FragmentReaderBinding? = null
|
||||
|
||||
var toolbar: Toolbar? = null
|
||||
|
||||
var drawerLayout: DrawerLayout? = null
|
||||
protected var tableDrawerRightContainer: NavigationView? = null
|
||||
var tabSwitcherRoot: View? = null
|
||||
|
||||
var activityMainRoot: View? = null
|
||||
|
||||
@JvmField
|
||||
@Inject
|
||||
@ -262,10 +242,6 @@ abstract class CoreReaderFragment :
|
||||
var painter: DarkModeViewPainter? = null
|
||||
protected var currentWebViewIndex by mutableStateOf(0)
|
||||
private var currentTtsWebViewIndex = 0
|
||||
protected var actionBar: ActionBar? = null
|
||||
protected var mainMenu: MainMenu? = null
|
||||
|
||||
private var tabRecyclerView: RecyclerView? = null
|
||||
private var isFirstTimeMainPageLoaded = true
|
||||
private var isFromManageExternalLaunch = false
|
||||
private val savingTabsMutex = Mutex()
|
||||
@ -295,7 +271,6 @@ abstract class CoreReaderFragment :
|
||||
private var documentParser: DocumentParser? = null
|
||||
private var tts: KiwixTextToSpeech? = null
|
||||
private var compatCallback: CompatFindActionModeCallback? = null
|
||||
private var tabsAdapter: TabsAdapter? = null
|
||||
private var zimReaderSource: ZimReaderSource? = null
|
||||
private var actionMode: ActionMode? = null
|
||||
private var tempWebViewForUndo: KiwixWebView? = null
|
||||
@ -304,7 +279,6 @@ abstract class CoreReaderFragment :
|
||||
private var isFirstRun = false
|
||||
private var tableDrawerAdapter: TableDrawerAdapter? = null
|
||||
private var tableDrawerRight: RecyclerView? = null
|
||||
private var tabCallback: ItemTouchHelper.Callback? = null
|
||||
private var donationLayout: FrameLayout? = null
|
||||
private var bookmarkingJob: Job? = null
|
||||
private var isBookmarked = false
|
||||
@ -517,53 +491,13 @@ abstract class CoreReaderFragment :
|
||||
activity?.let {
|
||||
WebView(it).destroy() // Workaround for buggy webViews see #710
|
||||
}
|
||||
prepareViews()
|
||||
handleLocaleCheck()
|
||||
activity?.setSupportActionBar(toolbar)
|
||||
actionBar = activity?.supportActionBar
|
||||
initHideBackToTopTimer()
|
||||
initTabCallback()
|
||||
toolbar?.setOnTouchListener(
|
||||
object : OnSwipeTouchListener(requireActivity()) {
|
||||
@SuppressLint("SyntheticAccessor")
|
||||
override fun onSwipeBottom() {
|
||||
showTabSwitcher()
|
||||
}
|
||||
|
||||
override fun onSwipeLeft() {
|
||||
if (currentWebViewIndex < webViewList.size - 1) {
|
||||
val current: View? = getCurrentWebView()
|
||||
startAnimation(current, R.anim.transition_left)
|
||||
selectTab(currentWebViewIndex + 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwipeRight() {
|
||||
if (currentWebViewIndex > 0) {
|
||||
val current: View? = getCurrentWebView()
|
||||
startAnimation(current, R.anim.transition_right)
|
||||
selectTab(currentWebViewIndex - 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTap(e: MotionEvent?) {
|
||||
e?.let {
|
||||
val titleTextView = toolbar?.findFirstTextView() ?: return@onTap
|
||||
titleTextView.let {
|
||||
// only initiate search if it is on the reader screen
|
||||
mainMenu?.tryExpandSearch(zimReaderContainer?.zimFileReader)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
loadDrawerViews()
|
||||
tableDrawerRight =
|
||||
tableDrawerRightContainer?.getHeaderView(0)?.findViewById(R.id.right_drawer_list)
|
||||
addFileReader()
|
||||
setupTabsAdapter()
|
||||
setTableDrawerInfo()
|
||||
setTabListener()
|
||||
activity?.let {
|
||||
compatCallback = CompatFindActionModeCallback(it)
|
||||
}
|
||||
@ -572,13 +506,6 @@ abstract class CoreReaderFragment :
|
||||
loadPrefs()
|
||||
updateTitle()
|
||||
handleIntentExtras(requireActivity().intent)
|
||||
tabRecyclerView?.let {
|
||||
it.adapter = tabsAdapter
|
||||
tabCallback?.let { callBack ->
|
||||
ItemTouchHelper(callBack).attachToRecyclerView(it)
|
||||
}
|
||||
}
|
||||
|
||||
// Only check intent on first start of activity. Otherwise the intents will enter infinite loops
|
||||
// when "Don't keep activities" is on.
|
||||
if (savedInstanceState == null) {
|
||||
@ -700,50 +627,6 @@ abstract class CoreReaderFragment :
|
||||
unsupportedMimeTypeHandler?.setAlertDialogShower(alertDialogShower as AlertDialogShower)
|
||||
}
|
||||
|
||||
private fun prepareViews() {
|
||||
fragmentReaderBinding?.let { readerBinding ->
|
||||
with(readerBinding.root) {
|
||||
activityMainRoot = findViewById(R.id.activity_main_root)
|
||||
toolbar = findViewById(R.id.toolbar)
|
||||
tabSwitcherRoot = findViewById(R.id.activity_main_tab_switcher)
|
||||
tabRecyclerView = findViewById(R.id.tab_switcher_recycler_view)
|
||||
donationLayout = findViewById(R.id.donation_layout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initTabCallback() {
|
||||
tabCallback = object : ItemTouchHelper.Callback() {
|
||||
override fun getMovementFlags(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int = makeMovementFlags(0, ItemTouchHelper.UP or ItemTouchHelper.DOWN)
|
||||
|
||||
override fun onChildDraw(
|
||||
c: Canvas,
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
dX: Float,
|
||||
dY: Float,
|
||||
actionState: Int,
|
||||
isCurrentlyActive: Boolean
|
||||
) {
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
|
||||
viewHolder.itemView.alpha = 1 - abs(dY) / viewHolder.itemView.measuredHeight
|
||||
}
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean = false
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
closeTab(viewHolder.adapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun initHideBackToTopTimer() {
|
||||
hideBackToTopTimer = object : CountDownTimer(1200, 1200) {
|
||||
@ -811,22 +694,6 @@ abstract class CoreReaderFragment :
|
||||
})
|
||||
}
|
||||
|
||||
private fun setTabListener() {
|
||||
tabsAdapter?.setTabClickListener(object : TabsAdapter.TabClickListener {
|
||||
override fun onSelectTab(view: View, position: Int) {
|
||||
hideTabSwitcher()
|
||||
selectTab(position)
|
||||
|
||||
// Bug Fix #592
|
||||
updateBottomToolbarArrowsAlpha()
|
||||
}
|
||||
|
||||
override fun onCloseTab(view: View, position: Int) {
|
||||
closeTab(position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setTableDrawerInfo() {
|
||||
tableDrawerRight?.apply {
|
||||
layoutManager = LinearLayoutManager(requireActivity())
|
||||
@ -836,22 +703,6 @@ abstract class CoreReaderFragment :
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTabsAdapter() {
|
||||
tabsAdapter = painter?.let {
|
||||
TabsAdapter(
|
||||
requireActivity() as AppCompatActivity,
|
||||
webViewList,
|
||||
it
|
||||
).apply {
|
||||
registerAdapterDataObserver(object : AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
readerMenuState?.updateTabIcon(itemCount)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFileReader() {
|
||||
documentParserJs = requireActivity().readFile("js/documentParser.js")
|
||||
documentSections?.clear()
|
||||
@ -901,7 +752,6 @@ abstract class CoreReaderFragment :
|
||||
)
|
||||
}
|
||||
showSearchPlaceHolderInToolbar(true)
|
||||
startAnimation(tabSwitcherRoot, R.anim.slide_down)
|
||||
readerMenuState?.showTabSwitcherOptions()
|
||||
}
|
||||
|
||||
@ -926,13 +776,6 @@ abstract class CoreReaderFragment :
|
||||
}
|
||||
}
|
||||
|
||||
protected fun startAnimation(
|
||||
view: View?,
|
||||
@AnimRes anim: Int
|
||||
) {
|
||||
view?.startAnimation(AnimationUtils.loadAnimation(view.context, anim))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shouldCloseZimBook A flag to indicate whether the ZIM book should be closed.
|
||||
* - Default is `true`, which ensures normal behavior for most scenarios.
|
||||
@ -1074,7 +917,7 @@ abstract class CoreReaderFragment :
|
||||
@Suppress("ReturnCount", "NestedBlockDepth")
|
||||
override fun onBackPressed(activity: AppCompatActivity): FragmentActivityExtensions.Super {
|
||||
when {
|
||||
tabSwitcherRoot?.visibility == VISIBLE -> {
|
||||
readerScreenState.value.showTabSwitcher -> {
|
||||
selectTab(
|
||||
if (currentWebViewIndex < webViewList.size) {
|
||||
currentWebViewIndex
|
||||
@ -1300,16 +1143,11 @@ abstract class CoreReaderFragment :
|
||||
}
|
||||
safelyCancelBookmarkJob()
|
||||
unBindViewsAndBinding()
|
||||
tabCallback = null
|
||||
hideBackToTopTimer?.cancel()
|
||||
hideBackToTopTimer = null
|
||||
stopOngoingLoadingAndClearWebViewList()
|
||||
actionBar = null
|
||||
mainMenu = null
|
||||
tabRecyclerView?.adapter = null
|
||||
tableDrawerRight?.adapter = null
|
||||
tableDrawerAdapter = null
|
||||
tabsAdapter = null
|
||||
tempWebViewListForUndo.clear()
|
||||
// create a base Activity class that class this.
|
||||
deleteCachedFiles(requireActivity())
|
||||
@ -1336,17 +1174,10 @@ abstract class CoreReaderFragment :
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun unBindViewsAndBinding() {
|
||||
activityMainRoot = null
|
||||
tabRecyclerView = null
|
||||
tabSwitcherRoot = null
|
||||
compatCallback?.finish()
|
||||
compatCallback = null
|
||||
toolbar?.setOnTouchListener(null)
|
||||
toolbar = null
|
||||
drawerLayout = null
|
||||
tableDrawerRightContainer = null
|
||||
fragmentReaderBinding?.root?.removeAllViews()
|
||||
fragmentReaderBinding = null
|
||||
donationLayout?.removeAllViews()
|
||||
donationLayout = null
|
||||
}
|
||||
@ -1522,18 +1353,7 @@ abstract class CoreReaderFragment :
|
||||
reopenBook()
|
||||
}
|
||||
tempWebViewForUndo?.let {
|
||||
if (tabSwitcherRoot?.visibility == GONE) {
|
||||
// Remove the top margin from the webView when the tabSwitcher is not visible.
|
||||
// We have added this margin in `TabsAdapter` to not show the top margin in tabs.
|
||||
// `tempWebViewForUndo` saved with that margin so before showing it to the `contentFrame`
|
||||
// We need to set full width and height for properly showing the content of webView.
|
||||
it.layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
webViewList.add(index, it)
|
||||
tabsAdapter?.notifyDataSetChanged()
|
||||
readerScreenState.value.snackBarHostState.snack(
|
||||
context?.getString(R.string.tab_restored).orEmpty(),
|
||||
lifecycleScope = lifecycleScope
|
||||
@ -1555,7 +1375,6 @@ abstract class CoreReaderFragment :
|
||||
currentWebViewIndex = position
|
||||
val webView = safelyGetWebView(position) ?: return
|
||||
safelyAddWebView(webView)
|
||||
tabsAdapter?.selected = currentWebViewIndex
|
||||
updateBottomToolbarVisibility()
|
||||
loadPrefs()
|
||||
updateUrlFlow()
|
||||
@ -1999,7 +1818,6 @@ abstract class CoreReaderFragment :
|
||||
private fun restoreDeletedTabs() {
|
||||
if (tempWebViewListForUndo.isNotEmpty()) {
|
||||
webViewList.addAll(tempWebViewListForUndo)
|
||||
tabsAdapter?.notifyDataSetChanged()
|
||||
readerScreenState.value.snackBarHostState.snack(
|
||||
context?.getString(R.string.tabs_restored).orEmpty(),
|
||||
lifecycleScope = lifecycleScope
|
||||
@ -2397,10 +2215,8 @@ abstract class CoreReaderFragment :
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
// Forcing redraw of RecyclerView children so that the tabs are properly oriented on rotation
|
||||
tabRecyclerView?.adapter = tabsAdapter
|
||||
// force redraw of donation layout if it is showing.
|
||||
if (donationLayout?.isVisible == true) {
|
||||
if (readerScreenState.value.shouldShowDonationPopup) {
|
||||
showDonationLayout()
|
||||
}
|
||||
}
|
||||
@ -2647,7 +2463,6 @@ abstract class CoreReaderFragment :
|
||||
return
|
||||
}
|
||||
updateTableOfContents()
|
||||
tabsAdapter?.notifyDataSetChanged()
|
||||
updateBottomToolbarArrowsAlpha()
|
||||
val zimFileReader = zimReaderContainer?.zimFileReader
|
||||
if (hasValidFileAndUrl(getCurrentWebView()?.url, zimFileReader)) {
|
||||
@ -2681,13 +2496,19 @@ abstract class CoreReaderFragment :
|
||||
private fun hasValidFileAndUrl(url: String?, zimFileReader: ZimFileReader?): Boolean =
|
||||
url != null && zimFileReader != null
|
||||
|
||||
override fun webViewFailedLoading(url: String) {
|
||||
override fun webViewFailedLoading(failingUrl: String) {
|
||||
if (isAdded) {
|
||||
// If a URL fails to load, update the bookmark toggle.
|
||||
// This fixes the scenario where the previous page is bookmarked and the next
|
||||
// page fails to load, ensuring the bookmark toggle is unset correctly.
|
||||
updateUrlFlow()
|
||||
Log.d(TAG_KIWIX, String.format(getString(R.string.error_article_url_not_found), url))
|
||||
Log.d(
|
||||
TAG_KIWIX,
|
||||
String.format(
|
||||
getString(R.string.error_article_url_not_found),
|
||||
failingUrl
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2707,7 +2528,6 @@ abstract class CoreReaderFragment :
|
||||
|
||||
override fun webViewTitleUpdated(title: String) {
|
||||
updateTabIcon(webViewList.size)
|
||||
tabsAdapter?.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
|
@ -89,7 +89,6 @@ import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalWindowInfo
|
||||
@ -97,6 +96,8 @@ import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.semantics.testTag
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@ -146,6 +147,10 @@ import org.kiwix.kiwixmobile.core.utils.ComposeDimens.TWO_DP
|
||||
import org.kiwix.kiwixmobile.core.utils.StyleUtils.fromHtml
|
||||
|
||||
const val CONTENT_LOADING_PROGRESSBAR_TESTING_TAG = "contentLoadingProgressBarTestingTag"
|
||||
const val TAB_SWITCHER_VIEW_TESTING_TAG = "tabSwitcherViewTestingTag"
|
||||
const val READER_SCREEN_TESTING_TAG = "readerScreenTestingTag"
|
||||
const val CLOSE_ALL_TABS_BUTTON_TESTING_TAG = "closeAllTabsButtonTestingTag"
|
||||
const val TAB_TITLE_TESTING_TAG = "tabTitleTestingTag"
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Suppress("ComposableLambdaParameterNaming")
|
||||
@ -157,10 +162,10 @@ fun ReaderScreen(
|
||||
navigationIcon: @Composable () -> Unit
|
||||
) {
|
||||
val bottomNavHeightInDp = with(LocalDensity.current) { state.bottomNavigationHeight.toDp() }
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val topAppBarScrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
|
||||
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
|
||||
LaunchedEffect(bottomAppBarScrollBehavior.state.heightOffset) {
|
||||
onBottomScrollOffsetChanged(scrollBehavior.state.heightOffset)
|
||||
onBottomScrollOffsetChanged(bottomAppBarScrollBehavior.state.heightOffset)
|
||||
}
|
||||
KiwixDialogTheme {
|
||||
Scaffold(
|
||||
@ -169,21 +174,21 @@ fun ReaderScreen(
|
||||
ReaderTopBar(
|
||||
state,
|
||||
actionMenuItems,
|
||||
scrollBehavior,
|
||||
topAppBarScrollBehavior,
|
||||
navigationIcon
|
||||
)
|
||||
},
|
||||
floatingActionButton = { BackToTopFab(state) },
|
||||
modifier = Modifier
|
||||
.systemBarsPadding()
|
||||
.nestedScroll(scrollBehavior.nestedScrollConnection)
|
||||
.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection)
|
||||
.padding(bottom = bottomNavHeightInDp)
|
||||
.semantics { testTag = READER_SCREEN_TESTING_TAG }
|
||||
) { paddingValues ->
|
||||
ReaderContentLayout(
|
||||
state,
|
||||
Modifier.padding(paddingValues),
|
||||
bottomAppBarScrollBehavior
|
||||
bottomAppBarScrollBehavior,
|
||||
topAppBarScrollBehavior
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -195,7 +200,7 @@ fun ReaderScreen(
|
||||
private fun ReaderTopBar(
|
||||
state: ReaderScreenState,
|
||||
actionMenuItems: List<ActionMenuItem>,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
topAppBarScrollBehavior: TopAppBarScrollBehavior,
|
||||
navigationIcon: @Composable () -> Unit,
|
||||
) {
|
||||
if (!state.shouldShowFullScreenMode && !state.fullScreenItem.first) {
|
||||
@ -203,7 +208,7 @@ private fun ReaderTopBar(
|
||||
title = if (state.showTabSwitcher) "" else state.readerScreenTitle,
|
||||
navigationIcon = navigationIcon,
|
||||
actionMenuItems = actionMenuItems,
|
||||
topAppBarScrollBehavior = scrollBehavior,
|
||||
topAppBarScrollBehavior = topAppBarScrollBehavior,
|
||||
searchBar =
|
||||
searchPlaceHolderIfActive(state.searchPlaceHolderItemForCustomApps)
|
||||
)
|
||||
@ -215,7 +220,8 @@ private fun ReaderTopBar(
|
||||
private fun ReaderContentLayout(
|
||||
state: ReaderScreenState,
|
||||
modifier: Modifier = Modifier,
|
||||
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior
|
||||
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior,
|
||||
topAppBarScrollBehavior: TopAppBarScrollBehavior
|
||||
) {
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
TabSwitcherAnimated(state)
|
||||
@ -225,7 +231,7 @@ private fun ReaderContentLayout(
|
||||
state.fullScreenItem.first -> ShowFullScreenView(state)
|
||||
|
||||
else -> {
|
||||
ShowZIMFileContent(state)
|
||||
ShowZIMFileContent(state, bottomAppBarScrollBehavior, topAppBarScrollBehavior)
|
||||
ShowProgressBarIfZIMFilePageIsLoading(state)
|
||||
Column(Modifier.align(Alignment.BottomCenter)) {
|
||||
TtsControls(state)
|
||||
@ -255,11 +261,11 @@ private fun TabSwitcherAnimated(state: ReaderScreenState) {
|
||||
val transitionSpec = remember {
|
||||
slideInVertically(
|
||||
initialOffsetY = { -it },
|
||||
animationSpec = tween(durationMillis = 300)
|
||||
animationSpec = tween(durationMillis = HIDE_TAB_SWITCHER_DELAY.toInt())
|
||||
) + fadeIn() togetherWith
|
||||
slideOutVertically(
|
||||
targetOffsetY = { -it },
|
||||
animationSpec = tween(durationMillis = 300)
|
||||
animationSpec = tween(durationMillis = HIDE_TAB_SWITCHER_DELAY.toInt())
|
||||
) + fadeOut()
|
||||
}
|
||||
|
||||
@ -267,7 +273,9 @@ private fun TabSwitcherAnimated(state: ReaderScreenState) {
|
||||
visible = state.showTabSwitcher,
|
||||
enter = transitionSpec.targetContentEnter,
|
||||
exit = transitionSpec.initialContentExit,
|
||||
modifier = Modifier.zIndex(1f),
|
||||
modifier = Modifier
|
||||
.zIndex(1f)
|
||||
.semantics { testTag = TAB_SWITCHER_VIEW_TESTING_TAG },
|
||||
) {
|
||||
TabSwitcherView(
|
||||
state.kiwixWebViewList,
|
||||
@ -349,8 +357,13 @@ private fun BoxScope.CloseFullScreenImageButton(
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun ShowZIMFileContent(state: ReaderScreenState) {
|
||||
private fun ShowZIMFileContent(
|
||||
state: ReaderScreenState,
|
||||
bottomAppBarScrollBehavior: BottomAppBarScrollBehavior,
|
||||
topAppBarScrollBehavior: TopAppBarScrollBehavior
|
||||
) {
|
||||
state.selectedWebView?.let { selectedWebView ->
|
||||
key(selectedWebView) {
|
||||
AndroidView(
|
||||
@ -359,14 +372,23 @@ private fun ShowZIMFileContent(state: ReaderScreenState) {
|
||||
FrameLayout(context).apply {
|
||||
// Ensure the WebView has no parent before adding
|
||||
(selectedWebView.parent as? ViewGroup)?.removeView(selectedWebView)
|
||||
selectedWebView.setOnScrollChangeListener(null)
|
||||
selectedWebView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
val deltaY = (scrollY - oldScrollY).toFloat()
|
||||
if (deltaY == 0f) return@setOnScrollChangeListener
|
||||
// SAFELY drive top and bottom app bars
|
||||
topAppBarScrollBehavior.state.heightOffset -= deltaY
|
||||
bottomAppBarScrollBehavior.state.heightOffset -= deltaY
|
||||
}
|
||||
selectedWebView.layoutParams = FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
addView(selectedWebView)
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState())
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
// TODO handle the unnecessary vertical scroll when webView page chnaged.
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -657,6 +679,7 @@ private fun BoxScope.CloseAllTabButton(onCloseAllTabs: () -> Unit) {
|
||||
.graphicsLayer {
|
||||
rotationZ = rotation
|
||||
}
|
||||
.semantics { testTag = CLOSE_ALL_TABS_BUTTON_TESTING_TAG }
|
||||
.clickable(
|
||||
enabled = !isAnimating,
|
||||
onClick = {
|
||||
@ -732,7 +755,8 @@ private fun TabItemHeader(
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.padding(end = FOUR_DP)
|
||||
.weight(1f),
|
||||
.weight(1f)
|
||||
.semantics { testTag = TAB_TITLE_TESTING_TAG },
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
IconButton(
|
||||
|
@ -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.
|
||||
-->
|
||||
|
||||
<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">
|
||||
|
||||
<item
|
||||
android:id="@+id/find_prev"
|
||||
android:icon="@drawable/action_find_previous"
|
||||
android:title="@string/previous"
|
||||
app:showAsAction="always" />
|
||||
app:showAsAction="always"
|
||||
tools:ignore="AlwaysShowAction" />
|
||||
<item
|
||||
android:id="@+id/find_next"
|
||||
android:icon="@drawable/action_find_next"
|
||||
|
Loading…
x
Reference in New Issue
Block a user