mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 10:46:53 -04:00
Merge pull request #4217 from SOUMEN-PAL/4159-migrate-help-fragment-to-compose
4159 migrate help fragment to compose
This commit is contained in:
commit
eeba4efdc6
@ -20,25 +20,32 @@ package org.kiwix.kiwixmobile.error
|
|||||||
|
|
||||||
import androidx.compose.ui.test.assertIsDisplayed
|
import androidx.compose.ui.test.assertIsDisplayed
|
||||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||||
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
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.StringId.TextId
|
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
|
import org.kiwix.kiwixmobile.core.help.SEND_DIAGNOSTIC_REPORT_TESTING_TAG
|
||||||
import org.kiwix.kiwixmobile.testutils.TestUtils
|
import org.kiwix.kiwixmobile.testutils.TestUtils
|
||||||
|
|
||||||
fun errorActivity(func: ErrorActivityRobot.() -> Unit) = ErrorActivityRobot().apply(func)
|
fun errorActivity(func: ErrorActivityRobot.() -> Unit) = ErrorActivityRobot().apply(func)
|
||||||
|
|
||||||
class ErrorActivityRobot : BaseRobot() {
|
class ErrorActivityRobot : BaseRobot() {
|
||||||
fun assertSendDiagnosticReportDisplayed() {
|
fun assertSendDiagnosticReportDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||||
// Wait a bit for properly visible the HelpFragment.
|
// Wait a bit for properly visible the HelpFragment.
|
||||||
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS.toLong())
|
BaristaSleepInteractions.sleep(TestUtils.TEST_PAUSE_MS.toLong())
|
||||||
isVisible(TextId(R.string.send_report))
|
composeTestRule.apply {
|
||||||
|
waitForIdle()
|
||||||
|
onNodeWithTag(SEND_DIAGNOSTIC_REPORT_TESTING_TAG).assertIsDisplayed()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnSendDiagnosticReport() {
|
fun clickOnSendDiagnosticReport(composeTestRule: ComposeContentTestRule) {
|
||||||
clickOn(TextId(R.string.send_report))
|
composeTestRule.apply {
|
||||||
|
waitForIdle()
|
||||||
|
onNodeWithTag(SEND_DIAGNOSTIC_REPORT_TESTING_TAG).performClick()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertErrorActivityDisplayed(composeTestRule: ComposeContentTestRule) {
|
fun assertErrorActivityDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||||
|
@ -91,8 +91,8 @@ class ErrorActivityTest : BaseActivityTest() {
|
|||||||
it.navigate(R.id.helpFragment)
|
it.navigate(R.id.helpFragment)
|
||||||
}
|
}
|
||||||
errorActivity {
|
errorActivity {
|
||||||
assertSendDiagnosticReportDisplayed()
|
assertSendDiagnosticReportDisplayed(composeTestRule)
|
||||||
clickOnSendDiagnosticReport()
|
clickOnSendDiagnosticReport(composeTestRule)
|
||||||
assertErrorActivityDisplayed(composeTestRule)
|
assertErrorActivityDisplayed(composeTestRule)
|
||||||
// Click on "No, Thanks" button to see it's functionality working or not.
|
// Click on "No, Thanks" button to see it's functionality working or not.
|
||||||
clickOnNoThanksButton(composeTestRule)
|
clickOnNoThanksButton(composeTestRule)
|
||||||
@ -101,9 +101,9 @@ class ErrorActivityTest : BaseActivityTest() {
|
|||||||
it.navigate(R.id.helpFragment)
|
it.navigate(R.id.helpFragment)
|
||||||
}
|
}
|
||||||
// Assert HelpFragment is visible or not after clicking on the "No, Thanks" button.
|
// Assert HelpFragment is visible or not after clicking on the "No, Thanks" button.
|
||||||
assertSendDiagnosticReportDisplayed()
|
assertSendDiagnosticReportDisplayed(composeTestRule)
|
||||||
// Again click on "Send diagnostic report" button to open the ErrorActivity.
|
// Again click on "Send diagnostic report" button to open the ErrorActivity.
|
||||||
clickOnSendDiagnosticReport()
|
clickOnSendDiagnosticReport(composeTestRule)
|
||||||
assertErrorActivityDisplayed(composeTestRule)
|
assertErrorActivityDisplayed(composeTestRule)
|
||||||
// Check check boxes are displayed or not.
|
// Check check boxes are displayed or not.
|
||||||
assertCheckBoxesDisplayed(composeTestRule)
|
assertCheckBoxesDisplayed(composeTestRule)
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
package org.kiwix.kiwixmobile.help
|
package org.kiwix.kiwixmobile.help
|
||||||
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import androidx.compose.ui.test.junit4.createComposeRule
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.espresso.IdlingRegistry
|
import androidx.test.espresso.IdlingRegistry
|
||||||
@ -33,6 +34,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.testutils.RetryRule
|
import org.kiwix.kiwixmobile.testutils.RetryRule
|
||||||
@ -43,6 +45,13 @@ import org.kiwix.kiwixmobile.utils.KiwixIdlingResource
|
|||||||
class HelpFragmentTest : BaseActivityTest() {
|
class HelpFragmentTest : BaseActivityTest() {
|
||||||
private lateinit var sharedPreferenceUtil: SharedPreferenceUtil
|
private lateinit var sharedPreferenceUtil: SharedPreferenceUtil
|
||||||
|
|
||||||
|
@Rule(order = RETRY_RULE_ORDER)
|
||||||
|
@JvmField
|
||||||
|
val retryRule = RetryRule()
|
||||||
|
|
||||||
|
@get:Rule(order = COMPOSE_TEST_RULE_ORDER)
|
||||||
|
val composeTestRule = createComposeRule()
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
override fun waitForIdle() {
|
override fun waitForIdle() {
|
||||||
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
|
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
|
||||||
@ -66,10 +75,6 @@ class HelpFragmentTest : BaseActivityTest() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Rule(order = RETRY_RULE_ORDER)
|
|
||||||
@JvmField
|
|
||||||
val retryRule = RetryRule()
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
AccessibilityChecks.enable().setRunChecksFromRootView(true)
|
AccessibilityChecks.enable().setRunChecksFromRootView(true)
|
||||||
}
|
}
|
||||||
@ -81,16 +86,16 @@ class HelpFragmentTest : BaseActivityTest() {
|
|||||||
it.navigate(R.id.helpFragment)
|
it.navigate(R.id.helpFragment)
|
||||||
}
|
}
|
||||||
help {
|
help {
|
||||||
clickOnWhatDoesKiwixDo()
|
clickOnWhatDoesKiwixDo(composeTestRule)
|
||||||
assertWhatDoesKiwixDoIsExpanded()
|
assertWhatDoesKiwixDoIsExpanded(composeTestRule)
|
||||||
clickOnWhatDoesKiwixDo()
|
clickOnWhatDoesKiwixDo(composeTestRule)
|
||||||
clickOnWhereIsContent()
|
clickOnWhereIsContent(composeTestRule)
|
||||||
assertWhereIsContentIsExpanded()
|
assertWhereIsContentIsExpanded(composeTestRule)
|
||||||
clickOnWhereIsContent()
|
clickOnWhereIsContent(composeTestRule)
|
||||||
clickOnHowToUpdateContent()
|
clickOnHowToUpdateContent(composeTestRule)
|
||||||
assertHowToUpdateContentIsExpanded()
|
assertHowToUpdateContentIsExpanded(composeTestRule)
|
||||||
clickOnHowToUpdateContent()
|
clickOnHowToUpdateContent(composeTestRule)
|
||||||
assertWhyCopyMoveFilesToAppPublicDirectoryIsNotVisible()
|
assertWhyCopyMoveFilesToAppPublicDirectoryIsNotVisible(composeTestRule)
|
||||||
}
|
}
|
||||||
LeakAssertions.assertNoLeaks()
|
LeakAssertions.assertNoLeaks()
|
||||||
}
|
}
|
||||||
@ -103,18 +108,18 @@ class HelpFragmentTest : BaseActivityTest() {
|
|||||||
it.navigate(R.id.helpFragment)
|
it.navigate(R.id.helpFragment)
|
||||||
}
|
}
|
||||||
help {
|
help {
|
||||||
clickOnWhatDoesKiwixDo()
|
clickOnWhatDoesKiwixDo(composeTestRule)
|
||||||
assertWhatDoesKiwixDoIsExpanded()
|
assertWhatDoesKiwixDoIsExpanded(composeTestRule)
|
||||||
clickOnWhatDoesKiwixDo()
|
clickOnWhatDoesKiwixDo(composeTestRule)
|
||||||
clickOnWhereIsContent()
|
clickOnWhereIsContent(composeTestRule)
|
||||||
assertWhereIsContentIsExpanded()
|
assertWhereIsContentIsExpanded(composeTestRule)
|
||||||
clickOnWhereIsContent()
|
clickOnWhereIsContent(composeTestRule)
|
||||||
clickOnHowToUpdateContent()
|
clickOnHowToUpdateContent(composeTestRule)
|
||||||
assertHowToUpdateContentIsExpanded()
|
assertHowToUpdateContentIsExpanded(composeTestRule)
|
||||||
clickOnHowToUpdateContent()
|
clickOnHowToUpdateContent(composeTestRule)
|
||||||
clickWhyCopyMoveFilesToAppPublicDirectory()
|
clickWhyCopyMoveFilesToAppPublicDirectory(composeTestRule)
|
||||||
assertWhyCopyMoveFilesToAppPublicDirectoryIsExpanded()
|
assertWhyCopyMoveFilesToAppPublicDirectoryIsExpanded(composeTestRule)
|
||||||
clickWhyCopyMoveFilesToAppPublicDirectory()
|
clickWhyCopyMoveFilesToAppPublicDirectory(composeTestRule)
|
||||||
}
|
}
|
||||||
LeakAssertions.assertNoLeaks()
|
LeakAssertions.assertNoLeaks()
|
||||||
}
|
}
|
||||||
|
@ -17,80 +17,128 @@
|
|||||||
*/
|
*/
|
||||||
package org.kiwix.kiwixmobile.help
|
package org.kiwix.kiwixmobile.help
|
||||||
|
|
||||||
|
import androidx.compose.ui.test.assertContentDescriptionEquals
|
||||||
|
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.performClick
|
||||||
import androidx.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
import org.kiwix.kiwixmobile.BaseRobot
|
import org.kiwix.kiwixmobile.BaseRobot
|
||||||
import org.kiwix.kiwixmobile.Findable.StringId.TextId
|
import org.kiwix.kiwixmobile.core.R
|
||||||
import org.kiwix.kiwixmobile.Findable.Text
|
|
||||||
import org.kiwix.kiwixmobile.Findable.ViewId
|
|
||||||
import org.kiwix.kiwixmobile.core.R.id
|
|
||||||
import org.kiwix.kiwixmobile.core.R.string
|
import org.kiwix.kiwixmobile.core.R.string
|
||||||
|
import org.kiwix.kiwixmobile.core.help.HELP_SCREEN_ITEM_DESCRIPTION_TESTING_TAG
|
||||||
|
import org.kiwix.kiwixmobile.core.help.HELP_SCREEN_ITEM_TITLE_TESTING_TAG
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.components.TOOLBAR_TITLE_TESTING_TAG
|
||||||
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView
|
||||||
|
|
||||||
fun help(func: HelpRobot.() -> Unit) = HelpRobot().apply(func)
|
fun help(func: HelpRobot.() -> Unit) = HelpRobot().apply(func)
|
||||||
|
|
||||||
class HelpRobot : BaseRobot() {
|
class HelpRobot : BaseRobot() {
|
||||||
fun assertToolbarDisplayed() {
|
fun assertToolbarDisplayed(composeTestRule: ComposeContentTestRule) {
|
||||||
isVisible(ViewId(id.toolbar))
|
composeTestRule.apply {
|
||||||
|
waitForIdle()
|
||||||
|
onNodeWithTag(TOOLBAR_TITLE_TESTING_TAG)
|
||||||
|
.assertTextEquals(context.getString(R.string.menu_help))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnWhatDoesKiwixDo() {
|
fun clickOnWhatDoesKiwixDo(composeTestRule: ComposeContentTestRule) {
|
||||||
testFlakyView({ onView(withText(string.help_2)).perform(click()) })
|
clickOnHelpScreenItemTitle(0, composeTestRule)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertWhatDoesKiwixDoIsExpanded() {
|
fun assertWhatDoesKiwixDoIsExpanded(composeTestRule: ComposeContentTestRule) {
|
||||||
isVisible(
|
assertHelpScreenDescriptionDisplayed(
|
||||||
Text(
|
helpTextFormat(string.help_3, string.help_4),
|
||||||
helpTextFormat(
|
composeTestRule
|
||||||
string.help_3,
|
|
||||||
string.help_4
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnWhereIsContent() {
|
fun clickOnWhereIsContent(composeTestRule: ComposeContentTestRule) {
|
||||||
clickOn(TextId(string.help_5))
|
clickOnHelpScreenItemTitle(1, composeTestRule)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertWhereIsContentIsExpanded() {
|
fun assertWhereIsContentIsExpanded(composeTestRule: ComposeContentTestRule) {
|
||||||
isVisible(
|
assertHelpScreenDescriptionDisplayed(
|
||||||
Text(
|
helpTextFormat(
|
||||||
helpTextFormat(
|
string.help_6,
|
||||||
string.help_6,
|
string.help_7,
|
||||||
string.help_7,
|
string.help_8,
|
||||||
string.help_8,
|
string.help_9,
|
||||||
string.help_9,
|
string.help_10,
|
||||||
string.help_10,
|
string.help_11
|
||||||
string.help_11
|
),
|
||||||
)
|
composeTestRule
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickOnHowToUpdateContent() {
|
fun clickOnHowToUpdateContent(composeTestRule: ComposeContentTestRule) {
|
||||||
clickOn(TextId(string.how_to_update_content))
|
clickOnHelpScreenItemTitle(2, composeTestRule)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertHowToUpdateContentIsExpanded() {
|
fun assertHowToUpdateContentIsExpanded(composeTestRule: ComposeContentTestRule) {
|
||||||
isVisible(TextId(string.update_content_description))
|
assertHelpScreenDescriptionDisplayed(
|
||||||
|
context.getString(string.update_content_description),
|
||||||
|
composeTestRule
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clickWhyCopyMoveFilesToAppPublicDirectory() {
|
fun clickWhyCopyMoveFilesToAppPublicDirectory(composeTestRule: ComposeContentTestRule) {
|
||||||
clickOn(TextId(string.why_copy_move_files_to_app_directory))
|
clickOnHelpScreenItemTitle(3, composeTestRule)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertWhyCopyMoveFilesToAppPublicDirectoryIsExpanded() {
|
fun assertWhyCopyMoveFilesToAppPublicDirectoryIsExpanded(composeTestRule: ComposeContentTestRule) {
|
||||||
isVisible(Text(context.getString(string.copy_move_files_to_app_directory_description)))
|
assertHelpScreenDescriptionDisplayed(
|
||||||
|
context.getString(string.copy_move_files_to_app_directory_description),
|
||||||
|
composeTestRule
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun assertWhyCopyMoveFilesToAppPublicDirectoryIsNotVisible() {
|
fun assertWhyCopyMoveFilesToAppPublicDirectoryIsNotVisible(composeTestRule: ComposeContentTestRule) {
|
||||||
|
composeTestRule.apply {
|
||||||
|
waitForIdle()
|
||||||
|
val itemTitleList = onAllNodesWithTag(HELP_SCREEN_ITEM_TITLE_TESTING_TAG)
|
||||||
|
val itemCount = itemTitleList.fetchSemanticsNodes().size
|
||||||
|
repeat(itemCount) { index ->
|
||||||
|
try {
|
||||||
|
itemTitleList[index]
|
||||||
|
.assertTextEquals(context.getString(string.why_copy_move_files_to_app_directory))
|
||||||
|
// If "Why copy/move files to app public directory?" item is visible throw the error.
|
||||||
|
throw RuntimeException("\"Why copy/move files to app public directory?\" help item is visible in non-playStore variant")
|
||||||
|
} catch (_: AssertionError) {
|
||||||
|
// If not found then nothing will do.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
onView(withText(string.why_copy_move_files_to_app_directory))
|
onView(withText(string.why_copy_move_files_to_app_directory))
|
||||||
.check(doesNotExist())
|
.check(doesNotExist())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun clickOnHelpScreenItemTitle(index: Int, composeTestRule: ComposeContentTestRule) {
|
||||||
|
testFlakyView({
|
||||||
|
composeTestRule.apply {
|
||||||
|
waitForIdle()
|
||||||
|
val itemTitleList = onAllNodesWithTag(HELP_SCREEN_ITEM_TITLE_TESTING_TAG)
|
||||||
|
itemTitleList[index].performClick()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun assertHelpScreenDescriptionDisplayed(
|
||||||
|
description: String,
|
||||||
|
composeTestRule: ComposeContentTestRule
|
||||||
|
) {
|
||||||
|
testFlakyView({
|
||||||
|
composeTestRule.apply {
|
||||||
|
waitForIdle()
|
||||||
|
onNodeWithTag(HELP_SCREEN_ITEM_DESCRIPTION_TESTING_TAG)
|
||||||
|
.assertContentDescriptionEquals(description)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private fun helpTextFormat(vararg stringIds: Int) =
|
private fun helpTextFormat(vararg stringIds: Int) =
|
||||||
stringIds.joinToString(separator = "\n", transform = context::getString)
|
stringIds.joinToString(separator = "\n", transform = context::getString)
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ class TopLevelDestinationTest : BaseActivityTest() {
|
|||||||
}
|
}
|
||||||
clickHostBooksOnSideNav(ZimHostRobot::assertMenuWifiHotspotDiplayed)
|
clickHostBooksOnSideNav(ZimHostRobot::assertMenuWifiHotspotDiplayed)
|
||||||
clickSettingsOnSideNav(SettingsRobot::assertMenuSettingsDisplayed)
|
clickSettingsOnSideNav(SettingsRobot::assertMenuSettingsDisplayed)
|
||||||
clickHelpOnSideNav(HelpRobot::assertToolbarDisplayed)
|
clickHelpOnSideNav { HelpRobot().assertToolbarDisplayed(composeTestRule) }
|
||||||
clickSupportKiwixOnSideNav()
|
clickSupportKiwixOnSideNav()
|
||||||
pressBack()
|
pressBack()
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ class GetContentShortcutTest {
|
|||||||
}
|
}
|
||||||
clickHostBooksOnSideNav(ZimHostRobot::assertMenuWifiHotspotDiplayed)
|
clickHostBooksOnSideNav(ZimHostRobot::assertMenuWifiHotspotDiplayed)
|
||||||
clickSettingsOnSideNav(SettingsRobot::assertMenuSettingsDisplayed)
|
clickSettingsOnSideNav(SettingsRobot::assertMenuSettingsDisplayed)
|
||||||
clickHelpOnSideNav(HelpRobot::assertToolbarDisplayed)
|
clickHelpOnSideNav { HelpRobot().assertToolbarDisplayed(composeTestRule) }
|
||||||
clickSupportKiwixOnSideNav()
|
clickSupportKiwixOnSideNav()
|
||||||
pressBack()
|
pressBack()
|
||||||
}
|
}
|
||||||
|
@ -129,8 +129,7 @@
|
|||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/helpFragment"
|
android:id="@+id/helpFragment"
|
||||||
android:name="org.kiwix.kiwixmobile.help.KiwixHelpFragment"
|
android:name="org.kiwix.kiwixmobile.help.KiwixHelpFragment"
|
||||||
android:label="HelpFragment"
|
android:label="HelpFragment" />
|
||||||
tools:layout="@layout/fragment_help" />
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/kiwixSettingsFragment"
|
android:id="@+id/kiwixSettingsFragment"
|
||||||
android:name="org.kiwix.kiwixmobile.settings.KiwixSettingsFragment"
|
android:name="org.kiwix.kiwixmobile.settings.KiwixSettingsFragment"
|
||||||
|
@ -20,7 +20,6 @@ package plugin
|
|||||||
|
|
||||||
import Config
|
import Config
|
||||||
import Libs
|
import Libs
|
||||||
import Versions
|
|
||||||
import com.android.build.api.dsl.CommonExtension
|
import com.android.build.api.dsl.CommonExtension
|
||||||
import com.android.build.gradle.BaseExtension
|
import com.android.build.gradle.BaseExtension
|
||||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
|
||||||
|
@ -1,74 +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.help
|
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
|
||||||
import android.text.method.LinkMovementMethod
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import androidx.core.view.isGone
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
|
|
||||||
import org.kiwix.kiwixmobile.core.databinding.ItemHelpBinding
|
|
||||||
import org.kiwix.kiwixmobile.core.utils.AnimationUtils.collapse
|
|
||||||
import org.kiwix.kiwixmobile.core.utils.AnimationUtils.expand
|
|
||||||
|
|
||||||
internal class HelpAdapter(titleDescriptionMap: Map<String, String>) :
|
|
||||||
RecyclerView.Adapter<HelpAdapter.Item>() {
|
|
||||||
private var helpItems = titleDescriptionMap.map { (key, value) -> HelpItem(key, value) }
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
|
||||||
parent: ViewGroup,
|
|
||||||
viewType: Int
|
|
||||||
): Item = Item(ItemHelpBinding.inflate(LayoutInflater.from(parent.context), parent, false))
|
|
||||||
|
|
||||||
override fun onBindViewHolder(
|
|
||||||
holder: Item,
|
|
||||||
position: Int
|
|
||||||
) {
|
|
||||||
holder.bind(helpItems[position])
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getItemCount(): Int = helpItems.size
|
|
||||||
|
|
||||||
internal inner class Item(private val itemHelpBinding: ItemHelpBinding) :
|
|
||||||
BaseViewHolder<HelpItem>(itemHelpBinding.root) {
|
|
||||||
@SuppressWarnings("MagicNumber")
|
|
||||||
fun toggleDescriptionVisibility() {
|
|
||||||
if (itemHelpBinding.itemHelpDescription.isGone) {
|
|
||||||
ObjectAnimator.ofFloat(itemHelpBinding.itemHelpToggleExpand, "rotation", 0f, 180f).start()
|
|
||||||
itemHelpBinding.itemHelpDescription.expand()
|
|
||||||
} else {
|
|
||||||
ObjectAnimator.ofFloat(itemHelpBinding.itemHelpToggleExpand, "rotation", 180f, 360f).start()
|
|
||||||
itemHelpBinding.itemHelpDescription.collapse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bind(item: HelpItem) {
|
|
||||||
itemHelpBinding.itemHelpTitle.setOnClickListener { toggleDescriptionVisibility() }
|
|
||||||
itemHelpBinding.itemHelpToggleExpand.setOnClickListener { toggleDescriptionVisibility() }
|
|
||||||
itemHelpBinding.itemHelpDescription.apply {
|
|
||||||
text = item.description
|
|
||||||
movementMethod = LinkMovementMethod.getInstance()
|
|
||||||
}
|
|
||||||
itemHelpBinding.itemHelpTitle.text = item.title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HelpItem(val title: String, val description: String)
|
|
@ -17,20 +17,16 @@
|
|||||||
*/
|
*/
|
||||||
package org.kiwix.kiwixmobile.core.help
|
package org.kiwix.kiwixmobile.core.help
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.appcompat.widget.Toolbar
|
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
|
||||||
import org.kiwix.kiwixmobile.core.R
|
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseFragment
|
import org.kiwix.kiwixmobile.core.base.BaseFragment
|
||||||
import org.kiwix.kiwixmobile.core.databinding.FragmentHelpBinding
|
|
||||||
import org.kiwix.kiwixmobile.core.error.DiagnosticReportActivity
|
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
|
|
||||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon
|
||||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -38,64 +34,47 @@ import javax.inject.Inject
|
|||||||
abstract class HelpFragment : BaseFragment() {
|
abstract class HelpFragment : BaseFragment() {
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var sharedPreferenceUtil: SharedPreferenceUtil
|
lateinit var sharedPreferenceUtil: SharedPreferenceUtil
|
||||||
private var fragmentHelpBinding: FragmentHelpBinding? = null
|
|
||||||
|
// Each subclass is responsible for providing its own raw data.
|
||||||
protected open fun rawTitleDescriptionMap(): List<Pair<Int, Any>> = emptyList()
|
protected open fun rawTitleDescriptionMap(): List<Pair<Int, Any>> = emptyList()
|
||||||
override val fragmentToolbar: Toolbar? by lazy {
|
|
||||||
fragmentHelpBinding?.root?.findViewById(R.id.toolbar)
|
|
||||||
}
|
|
||||||
override val fragmentTitle: String? by lazy { getString(R.string.menu_help) }
|
|
||||||
|
|
||||||
private val titleDescriptionMap by lazy {
|
|
||||||
rawTitleDescriptionMap().associate { (title, description) ->
|
|
||||||
val descriptionValue =
|
|
||||||
when (description) {
|
|
||||||
is String -> description
|
|
||||||
is Int -> resources.getStringArray(description).joinToString(separator = "\n")
|
|
||||||
else -> {
|
|
||||||
throw IllegalArgumentException("Invalid description resource type for title: $title")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getString(title) to descriptionValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun inject(baseActivity: BaseActivity) {
|
override fun inject(baseActivity: BaseActivity) {
|
||||||
(baseActivity as CoreMainActivity).cachedComponent.inject(this)
|
(baseActivity as CoreMainActivity).cachedComponent.inject(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
val activity = requireActivity() as AppCompatActivity
|
|
||||||
fragmentHelpBinding?.activityHelpDiagnosticImageView?.setOnClickListener {
|
|
||||||
sendDiagnosticReport()
|
|
||||||
}
|
|
||||||
fragmentHelpBinding?.activityHelpDiagnosticTextView?.setOnClickListener {
|
|
||||||
sendDiagnosticReport()
|
|
||||||
}
|
|
||||||
fragmentHelpBinding?.activityHelpRecyclerView?.addItemDecoration(
|
|
||||||
DividerItemDecoration(activity, DividerItemDecoration.VERTICAL)
|
|
||||||
)
|
|
||||||
fragmentHelpBinding?.activityHelpRecyclerView?.adapter = HelpAdapter(titleDescriptionMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? = ComposeView(requireContext()).apply {
|
||||||
fragmentHelpBinding =
|
setContent {
|
||||||
FragmentHelpBinding.inflate(inflater, container, false)
|
// Create the helpScreen data using your rawTitleDescriptionMap.
|
||||||
return fragmentHelpBinding?.root
|
val helpScreenData = transformToHelpScreenData(
|
||||||
}
|
requireContext(),
|
||||||
|
rawTitleDescriptionMap()
|
||||||
private fun sendDiagnosticReport() {
|
)
|
||||||
requireActivity().start<DiagnosticReportActivity>()
|
// Call your HelpScreen composable.
|
||||||
}
|
HelpScreen(data = helpScreenData) {
|
||||||
|
NavigationIcon(onClick = { activity?.onBackPressedDispatcher?.onBackPressed() })
|
||||||
override fun onDestroyView() {
|
}
|
||||||
super.onDestroyView()
|
}
|
||||||
fragmentHelpBinding?.root?.removeAllViews()
|
}
|
||||||
fragmentHelpBinding = null
|
}
|
||||||
|
|
||||||
|
// Util function to modify the data accordingly
|
||||||
|
fun transformToHelpScreenData(
|
||||||
|
context: Context,
|
||||||
|
rawTitleDescriptionMap: List<Pair<Int, Any>>
|
||||||
|
): List<HelpScreenItemDataClass> {
|
||||||
|
return rawTitleDescriptionMap.map { (titleResId, description) ->
|
||||||
|
val title = context.getString(titleResId)
|
||||||
|
val descriptionValue = when (description) {
|
||||||
|
is String -> description
|
||||||
|
is Int -> context.resources.getStringArray(description).joinToString(separator = "\n")
|
||||||
|
else -> {
|
||||||
|
throw IllegalArgumentException("Invalid description resource type for title: $titleResId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HelpScreenItemDataClass(title, descriptionValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
124
core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt
Normal file
124
core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (c) 2025 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.help
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.HorizontalDivider
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Scaffold
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.minimumInteractiveComponentSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import org.kiwix.kiwixmobile.core.R
|
||||||
|
import org.kiwix.kiwixmobile.core.error.DiagnosticReportActivity
|
||||||
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.components.KiwixAppBar
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.MineShaftGray350
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.MineShaftGray600
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.HELP_SCREEN_DIVIDER_HEIGHT
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
|
||||||
|
|
||||||
|
const val SEND_DIAGNOSTIC_REPORT_TESTING_TAG = "sendDiagnosticReportTestingTag"
|
||||||
|
const val HELP_SCREEN_ITEM_TITLE_TESTING_TAG = "helpScreenItemTitleTestingTag"
|
||||||
|
const val HELP_SCREEN_ITEM_DESCRIPTION_TESTING_TAG = "helpScreenItemDescriptionTestingTag"
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Suppress("ComposableLambdaParameterNaming")
|
||||||
|
@Composable
|
||||||
|
fun HelpScreen(
|
||||||
|
data: List<HelpScreenItemDataClass>,
|
||||||
|
navigationIcon: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
val dividerColor = if (isSystemInDarkTheme()) {
|
||||||
|
MineShaftGray600
|
||||||
|
} else {
|
||||||
|
MineShaftGray350
|
||||||
|
}
|
||||||
|
KiwixTheme {
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
KiwixAppBar(R.string.menu_help, navigationIcon)
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Column(modifier = Modifier.padding(innerPadding)) {
|
||||||
|
SendReportRow()
|
||||||
|
HorizontalDivider(color = dividerColor, thickness = HELP_SCREEN_DIVIDER_HEIGHT)
|
||||||
|
HelpItemList(data, dividerColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SendReportRow() {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val isDarkTheme = isSystemInDarkTheme()
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { (context as? Activity)?.start<DiagnosticReportActivity>() }
|
||||||
|
.testTag(SEND_DIAGNOSTIC_REPORT_TESTING_TAG),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Start
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.ic_feedback_orange_24dp),
|
||||||
|
contentDescription = stringResource(R.string.send_report),
|
||||||
|
modifier = Modifier.padding(SIXTEEN_DP)
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.send_report),
|
||||||
|
color = if (isDarkTheme) Color.LightGray else Color.DarkGray,
|
||||||
|
style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Normal),
|
||||||
|
modifier = Modifier.minimumInteractiveComponentSize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HelpItemList(data: List<HelpScreenItemDataClass>, dividerColor: Color) {
|
||||||
|
LazyColumn(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
itemsIndexed(data, key = { _, item -> item.title }) { _, item ->
|
||||||
|
HelpScreenItem(data = item)
|
||||||
|
HorizontalDivider(color = dividerColor, thickness = HELP_SCREEN_DIVIDER_HEIGHT)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (c) 2025 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.help
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.text.method.LinkMovementMethod
|
||||||
|
import android.text.util.Linkify
|
||||||
|
import android.view.Gravity
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.defaultMinSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.minimumInteractiveComponentSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
|
import androidx.compose.ui.graphics.toArgb
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.testTag
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.text.util.LinkifyCompat
|
||||||
|
import org.kiwix.kiwixmobile.core.R
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.theme.MineShaftGray900
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.HELP_SCREEN_ARROW_ICON_SIZE
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.HELP_SCREEN_ITEM_TITLE_LETTER_SPACING
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.HELP_SCREEN_ITEM_TITLE_TEXT_SIZE
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
|
||||||
|
|
||||||
|
private const val HELP_ITEM_ANIMATION_DURATION = 300
|
||||||
|
private const val HELP_ITEM_ARROW_ROTATION_OPEN = 180f
|
||||||
|
private const val HELP_ITEM_ARROW_ROTATION_CLOSE = 0f
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HelpScreenItem(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
data: HelpScreenItemDataClass,
|
||||||
|
initiallyOpened: Boolean = false
|
||||||
|
) {
|
||||||
|
var isOpen by remember { mutableStateOf(initiallyOpened) }
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(vertical = EIGHT_DP, horizontal = SIXTEEN_DP),
|
||||||
|
verticalArrangement = Arrangement.Center,
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {
|
||||||
|
HelpItemHeader(data.title, isOpen) { isOpen = !isOpen }
|
||||||
|
AnimatedVisibility(visible = isOpen) {
|
||||||
|
Spacer(modifier = Modifier.height(EIGHT_DP))
|
||||||
|
HelpItemDescription(LocalContext.current, data.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HelpItemHeader(
|
||||||
|
title: String,
|
||||||
|
isOpen: Boolean,
|
||||||
|
onToggle: () -> Unit
|
||||||
|
) {
|
||||||
|
val arrowRotation by animateFloatAsState(
|
||||||
|
targetValue = if (isOpen) HELP_ITEM_ARROW_ROTATION_OPEN else HELP_ITEM_ARROW_ROTATION_CLOSE,
|
||||||
|
animationSpec = tween(HELP_ITEM_ANIMATION_DURATION),
|
||||||
|
label = "arrowRotation"
|
||||||
|
)
|
||||||
|
val interactionSource = remember(::MutableInteractionSource)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable(interactionSource = interactionSource, indication = null, onClick = onToggle)
|
||||||
|
.testTag(HELP_SCREEN_ITEM_TITLE_TESTING_TAG)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
fontSize = HELP_SCREEN_ITEM_TITLE_TEXT_SIZE,
|
||||||
|
style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.Medium),
|
||||||
|
letterSpacing = HELP_SCREEN_ITEM_TITLE_LETTER_SPACING,
|
||||||
|
modifier = Modifier.minimumInteractiveComponentSize()
|
||||||
|
)
|
||||||
|
Image(
|
||||||
|
imageVector = Icons.Default.KeyboardArrowDown,
|
||||||
|
contentDescription = stringResource(R.string.expand),
|
||||||
|
modifier = Modifier
|
||||||
|
.graphicsLayer {
|
||||||
|
rotationZ = arrowRotation
|
||||||
|
}
|
||||||
|
.defaultMinSize(
|
||||||
|
minWidth = HELP_SCREEN_ARROW_ICON_SIZE,
|
||||||
|
minHeight = HELP_SCREEN_ARROW_ICON_SIZE
|
||||||
|
)
|
||||||
|
.minimumInteractiveComponentSize(),
|
||||||
|
colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun HelpItemDescription(context: Context, description: String) {
|
||||||
|
val textColor = if (isSystemInDarkTheme()) {
|
||||||
|
Color.LightGray
|
||||||
|
} else {
|
||||||
|
MineShaftGray900
|
||||||
|
}
|
||||||
|
val helpItemDescription = remember { TextView(context) }
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = SIXTEEN_DP)
|
||||||
|
) {
|
||||||
|
AndroidView(
|
||||||
|
factory = { helpItemDescription },
|
||||||
|
modifier = Modifier.padding(bottom = SIXTEEN_DP)
|
||||||
|
.testTag(HELP_SCREEN_ITEM_DESCRIPTION_TESTING_TAG)
|
||||||
|
.semantics { contentDescription = description }
|
||||||
|
) { textView ->
|
||||||
|
textView.apply {
|
||||||
|
text = description
|
||||||
|
setTextAppearance(R.style.TextAppearance_KiwixTheme_Subtitle2)
|
||||||
|
setTextColor(textColor.toArgb())
|
||||||
|
minHeight =
|
||||||
|
context.resources.getDimensionPixelSize(R.dimen.material_minimum_height_and_width)
|
||||||
|
gravity = Gravity.CENTER or Gravity.START
|
||||||
|
LinkifyCompat.addLinks(this, Linkify.WEB_URLS)
|
||||||
|
movementMethod = LinkMovementMethod.getInstance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Kiwix Android
|
||||||
|
* Copyright (c) 2025 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.help
|
||||||
|
|
||||||
|
// Same as HelpItem data class in earlier in XML
|
||||||
|
data class HelpScreenItemDataClass(val title: String, val description: String)
|
@ -59,6 +59,7 @@ abstract class CoreSettingsFragment : BaseFragment() {
|
|||||||
requireActivity().supportFragmentManager.beginTransaction().remove(prefsFragment)
|
requireActivity().supportFragmentManager.beginTransaction().remove(prefsFragment)
|
||||||
.commitNowAllowingStateLoss()
|
.commitNowAllowingStateLoss()
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
settingsBinding?.root?.removeAllViews()
|
||||||
settingsBinding = null
|
settingsBinding = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,4 +98,10 @@ object ComposeDimens {
|
|||||||
|
|
||||||
// LocalLibraryFragment dimens
|
// LocalLibraryFragment dimens
|
||||||
val FAB_ICON_BOTTOM_MARGIN = 50.dp
|
val FAB_ICON_BOTTOM_MARGIN = 50.dp
|
||||||
|
|
||||||
|
// HelpFragment dimens
|
||||||
|
val HELP_SCREEN_DIVIDER_HEIGHT = 0.7.dp
|
||||||
|
val HELP_SCREEN_ITEM_TITLE_TEXT_SIZE = 20.sp
|
||||||
|
val HELP_SCREEN_ITEM_TITLE_LETTER_SPACING = 0.0125.em
|
||||||
|
val HELP_SCREEN_ARROW_ICON_SIZE = 35.dp
|
||||||
}
|
}
|
||||||
|
@ -1,54 +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="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
tools:context=".help.HelpFragment">
|
|
||||||
|
|
||||||
<include layout="@layout/layout_standard_app_bar" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/activity_help_diagnostic_image_view"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:contentDescription="@string/send_report"
|
|
||||||
android:padding="@dimen/activity_horizontal_margin"
|
|
||||||
android:src="@drawable/ic_feedback_orange_24dp"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@+id/app_bar" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/activity_help_diagnostic_text_view"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:drawablePadding="@dimen/activity_horizontal_margin"
|
|
||||||
android:gravity="start|center"
|
|
||||||
android:minHeight="@dimen/material_minimum_height_and_width"
|
|
||||||
android:text="@string/send_report"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/activity_help_diagnostic_image_view"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toEndOf="@id/activity_help_diagnostic_image_view"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/activity_help_diagnostic_image_view" />
|
|
||||||
|
|
||||||
<View
|
|
||||||
android:id="@+id/activity_help_diagnostic_divider"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="1dp"
|
|
||||||
android:background="?android:attr/listDivider"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/activity_help_diagnostic_image_view" />
|
|
||||||
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
|
||||||
android:id="@+id/activity_help_recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:clipToPadding="false"
|
|
||||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/activity_help_diagnostic_divider"
|
|
||||||
tools:listitem="@layout/item_help" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -9,8 +9,7 @@
|
|||||||
android:id="@+id/app_bar"
|
android:id="@+id/app_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
tools:showIn="@layout/fragment_help">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
|
@ -1,50 +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="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="@dimen/activity_horizontal_margin"
|
|
||||||
android:paddingTop="@dimen/dimen_medium_padding"
|
|
||||||
android:paddingEnd="@dimen/activity_horizontal_margin"
|
|
||||||
android:paddingBottom="@dimen/dimen_medium_padding">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/item_help_title"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="start|center"
|
|
||||||
android:minHeight="@dimen/material_minimum_height_and_width"
|
|
||||||
android:textAppearance="?textAppearanceHeadline6"
|
|
||||||
app:layout_constraintEnd_toStartOf="@id/item_help_toggle_expand"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
tools:text="@string/help_2" />
|
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/item_help_toggle_expand"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:contentDescription="@string/expand"
|
|
||||||
android:minWidth="@dimen/material_minimum_height_and_width"
|
|
||||||
android:minHeight="@dimen/material_minimum_height_and_width"
|
|
||||||
android:scaleType="centerInside"
|
|
||||||
android:src="@drawable/action_find_next"
|
|
||||||
app:layout_constraintBottom_toBottomOf="@id/item_help_title"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="@id/item_help_title"
|
|
||||||
app:tint="?colorOnSurface" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/item_help_description"
|
|
||||||
style="@style/list_item_body"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:autoLink="web"
|
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
|
||||||
android:textColor="?textSecondary"
|
|
||||||
android:visibility="gone"
|
|
||||||
app:layout_constraintTop_toBottomOf="@id/item_help_title"
|
|
||||||
tools:text="@string/help_3"
|
|
||||||
tools:visibility="visible" />
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -6,8 +6,7 @@
|
|||||||
android:id="@+id/app_bar"
|
android:id="@+id/app_bar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
tools:showIn="@layout/fragment_help">
|
|
||||||
|
|
||||||
<include layout="@layout/layout_toolbar" />
|
<include layout="@layout/layout_toolbar" />
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user