diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/error/ErrorActivityRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/error/ErrorActivityRobot.kt
index 66a1b2b32..0bb489cda 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/error/ErrorActivityRobot.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/error/ErrorActivityRobot.kt
@@ -20,25 +20,32 @@ package org.kiwix.kiwixmobile.error
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.ComposeContentTestRule
+import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import com.adevinta.android.barista.interaction.BaristaSleepInteractions
import org.kiwix.kiwixmobile.BaseRobot
-import org.kiwix.kiwixmobile.Findable.StringId.TextId
import org.kiwix.kiwixmobile.core.R
+import org.kiwix.kiwixmobile.core.help.SEND_DIAGNOSTIC_REPORT_TESTING_TAG
import org.kiwix.kiwixmobile.testutils.TestUtils
fun errorActivity(func: ErrorActivityRobot.() -> Unit) = ErrorActivityRobot().apply(func)
class ErrorActivityRobot : BaseRobot() {
- fun assertSendDiagnosticReportDisplayed() {
+ fun assertSendDiagnosticReportDisplayed(composeTestRule: ComposeContentTestRule) {
// Wait a bit for properly visible the HelpFragment.
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() {
- clickOn(TextId(R.string.send_report))
+ fun clickOnSendDiagnosticReport(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitForIdle()
+ onNodeWithTag(SEND_DIAGNOSTIC_REPORT_TESTING_TAG).performClick()
+ }
}
fun assertErrorActivityDisplayed(composeTestRule: ComposeContentTestRule) {
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/error/ErrorActivityTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/error/ErrorActivityTest.kt
index 13b683765..d4ae7474a 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/error/ErrorActivityTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/error/ErrorActivityTest.kt
@@ -91,8 +91,8 @@ class ErrorActivityTest : BaseActivityTest() {
it.navigate(R.id.helpFragment)
}
errorActivity {
- assertSendDiagnosticReportDisplayed()
- clickOnSendDiagnosticReport()
+ assertSendDiagnosticReportDisplayed(composeTestRule)
+ clickOnSendDiagnosticReport(composeTestRule)
assertErrorActivityDisplayed(composeTestRule)
// Click on "No, Thanks" button to see it's functionality working or not.
clickOnNoThanksButton(composeTestRule)
@@ -101,9 +101,9 @@ class ErrorActivityTest : BaseActivityTest() {
it.navigate(R.id.helpFragment)
}
// 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.
- clickOnSendDiagnosticReport()
+ clickOnSendDiagnosticReport(composeTestRule)
assertErrorActivityDisplayed(composeTestRule)
// Check check boxes are displayed or not.
assertCheckBoxesDisplayed(composeTestRule)
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpFragmentTest.kt
index ad09c89ea..f3b3b2385 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpFragmentTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpFragmentTest.kt
@@ -18,6 +18,7 @@
package org.kiwix.kiwixmobile.help
import android.os.Build
+import androidx.compose.ui.test.junit4.createComposeRule
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import androidx.test.espresso.IdlingRegistry
@@ -33,6 +34,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.testutils.RetryRule
@@ -43,6 +45,13 @@ import org.kiwix.kiwixmobile.utils.KiwixIdlingResource
class HelpFragmentTest : BaseActivityTest() {
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
override fun waitForIdle() {
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()).apply {
@@ -66,10 +75,6 @@ class HelpFragmentTest : BaseActivityTest() {
}
}
- @Rule(order = RETRY_RULE_ORDER)
- @JvmField
- val retryRule = RetryRule()
-
init {
AccessibilityChecks.enable().setRunChecksFromRootView(true)
}
@@ -81,16 +86,16 @@ class HelpFragmentTest : BaseActivityTest() {
it.navigate(R.id.helpFragment)
}
help {
- clickOnWhatDoesKiwixDo()
- assertWhatDoesKiwixDoIsExpanded()
- clickOnWhatDoesKiwixDo()
- clickOnWhereIsContent()
- assertWhereIsContentIsExpanded()
- clickOnWhereIsContent()
- clickOnHowToUpdateContent()
- assertHowToUpdateContentIsExpanded()
- clickOnHowToUpdateContent()
- assertWhyCopyMoveFilesToAppPublicDirectoryIsNotVisible()
+ clickOnWhatDoesKiwixDo(composeTestRule)
+ assertWhatDoesKiwixDoIsExpanded(composeTestRule)
+ clickOnWhatDoesKiwixDo(composeTestRule)
+ clickOnWhereIsContent(composeTestRule)
+ assertWhereIsContentIsExpanded(composeTestRule)
+ clickOnWhereIsContent(composeTestRule)
+ clickOnHowToUpdateContent(composeTestRule)
+ assertHowToUpdateContentIsExpanded(composeTestRule)
+ clickOnHowToUpdateContent(composeTestRule)
+ assertWhyCopyMoveFilesToAppPublicDirectoryIsNotVisible(composeTestRule)
}
LeakAssertions.assertNoLeaks()
}
@@ -103,18 +108,18 @@ class HelpFragmentTest : BaseActivityTest() {
it.navigate(R.id.helpFragment)
}
help {
- clickOnWhatDoesKiwixDo()
- assertWhatDoesKiwixDoIsExpanded()
- clickOnWhatDoesKiwixDo()
- clickOnWhereIsContent()
- assertWhereIsContentIsExpanded()
- clickOnWhereIsContent()
- clickOnHowToUpdateContent()
- assertHowToUpdateContentIsExpanded()
- clickOnHowToUpdateContent()
- clickWhyCopyMoveFilesToAppPublicDirectory()
- assertWhyCopyMoveFilesToAppPublicDirectoryIsExpanded()
- clickWhyCopyMoveFilesToAppPublicDirectory()
+ clickOnWhatDoesKiwixDo(composeTestRule)
+ assertWhatDoesKiwixDoIsExpanded(composeTestRule)
+ clickOnWhatDoesKiwixDo(composeTestRule)
+ clickOnWhereIsContent(composeTestRule)
+ assertWhereIsContentIsExpanded(composeTestRule)
+ clickOnWhereIsContent(composeTestRule)
+ clickOnHowToUpdateContent(composeTestRule)
+ assertHowToUpdateContentIsExpanded(composeTestRule)
+ clickOnHowToUpdateContent(composeTestRule)
+ clickWhyCopyMoveFilesToAppPublicDirectory(composeTestRule)
+ assertWhyCopyMoveFilesToAppPublicDirectoryIsExpanded(composeTestRule)
+ clickWhyCopyMoveFilesToAppPublicDirectory(composeTestRule)
}
LeakAssertions.assertNoLeaks()
}
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpRobot.kt
index 63a96e654..f1991e2c5 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpRobot.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpRobot.kt
@@ -17,80 +17,128 @@
*/
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.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.kiwix.kiwixmobile.BaseRobot
-import org.kiwix.kiwixmobile.Findable.StringId.TextId
-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
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
fun help(func: HelpRobot.() -> Unit) = HelpRobot().apply(func)
class HelpRobot : BaseRobot() {
- fun assertToolbarDisplayed() {
- isVisible(ViewId(id.toolbar))
+ fun assertToolbarDisplayed(composeTestRule: ComposeContentTestRule) {
+ composeTestRule.apply {
+ waitForIdle()
+ onNodeWithTag(TOOLBAR_TITLE_TESTING_TAG)
+ .assertTextEquals(context.getString(R.string.menu_help))
+ }
}
- fun clickOnWhatDoesKiwixDo() {
- testFlakyView({ onView(withText(string.help_2)).perform(click()) })
+ fun clickOnWhatDoesKiwixDo(composeTestRule: ComposeContentTestRule) {
+ clickOnHelpScreenItemTitle(0, composeTestRule)
}
- fun assertWhatDoesKiwixDoIsExpanded() {
- isVisible(
- Text(
- helpTextFormat(
- string.help_3,
- string.help_4
- )
- )
+ fun assertWhatDoesKiwixDoIsExpanded(composeTestRule: ComposeContentTestRule) {
+ assertHelpScreenDescriptionDisplayed(
+ helpTextFormat(string.help_3, string.help_4),
+ composeTestRule
)
}
- fun clickOnWhereIsContent() {
- clickOn(TextId(string.help_5))
+ fun clickOnWhereIsContent(composeTestRule: ComposeContentTestRule) {
+ clickOnHelpScreenItemTitle(1, composeTestRule)
}
- fun assertWhereIsContentIsExpanded() {
- isVisible(
- Text(
- helpTextFormat(
- string.help_6,
- string.help_7,
- string.help_8,
- string.help_9,
- string.help_10,
- string.help_11
- )
- )
+ fun assertWhereIsContentIsExpanded(composeTestRule: ComposeContentTestRule) {
+ assertHelpScreenDescriptionDisplayed(
+ helpTextFormat(
+ string.help_6,
+ string.help_7,
+ string.help_8,
+ string.help_9,
+ string.help_10,
+ string.help_11
+ ),
+ composeTestRule
)
}
- fun clickOnHowToUpdateContent() {
- clickOn(TextId(string.how_to_update_content))
+ fun clickOnHowToUpdateContent(composeTestRule: ComposeContentTestRule) {
+ clickOnHelpScreenItemTitle(2, composeTestRule)
}
- fun assertHowToUpdateContentIsExpanded() {
- isVisible(TextId(string.update_content_description))
+ fun assertHowToUpdateContentIsExpanded(composeTestRule: ComposeContentTestRule) {
+ assertHelpScreenDescriptionDisplayed(
+ context.getString(string.update_content_description),
+ composeTestRule
+ )
}
- fun clickWhyCopyMoveFilesToAppPublicDirectory() {
- clickOn(TextId(string.why_copy_move_files_to_app_directory))
+ fun clickWhyCopyMoveFilesToAppPublicDirectory(composeTestRule: ComposeContentTestRule) {
+ clickOnHelpScreenItemTitle(3, composeTestRule)
}
- fun assertWhyCopyMoveFilesToAppPublicDirectoryIsExpanded() {
- isVisible(Text(context.getString(string.copy_move_files_to_app_directory_description)))
+ fun assertWhyCopyMoveFilesToAppPublicDirectoryIsExpanded(composeTestRule: ComposeContentTestRule) {
+ 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))
.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) =
stringIds.joinToString(separator = "\n", transform = context::getString)
}
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt
index 07f24ba55..9b60a862c 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/main/TopLevelDestinationTest.kt
@@ -130,7 +130,7 @@ class TopLevelDestinationTest : BaseActivityTest() {
}
clickHostBooksOnSideNav(ZimHostRobot::assertMenuWifiHotspotDiplayed)
clickSettingsOnSideNav(SettingsRobot::assertMenuSettingsDisplayed)
- clickHelpOnSideNav(HelpRobot::assertToolbarDisplayed)
+ clickHelpOnSideNav { HelpRobot().assertToolbarDisplayed(composeTestRule) }
clickSupportKiwixOnSideNav()
pressBack()
}
diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/shortcuts/GetContentShortcutTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/shortcuts/GetContentShortcutTest.kt
index 800b8ef16..e50f27be0 100644
--- a/app/src/androidTest/java/org/kiwix/kiwixmobile/shortcuts/GetContentShortcutTest.kt
+++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/shortcuts/GetContentShortcutTest.kt
@@ -134,7 +134,7 @@ class GetContentShortcutTest {
}
clickHostBooksOnSideNav(ZimHostRobot::assertMenuWifiHotspotDiplayed)
clickSettingsOnSideNav(SettingsRobot::assertMenuSettingsDisplayed)
- clickHelpOnSideNav(HelpRobot::assertToolbarDisplayed)
+ clickHelpOnSideNav { HelpRobot().assertToolbarDisplayed(composeTestRule) }
clickSupportKiwixOnSideNav()
pressBack()
}
diff --git a/app/src/main/res/navigation/kiwix_nav_graph.xml b/app/src/main/res/navigation/kiwix_nav_graph.xml
index 60f5534e0..13be224cf 100644
--- a/app/src/main/res/navigation/kiwix_nav_graph.xml
+++ b/app/src/main/res/navigation/kiwix_nav_graph.xml
@@ -129,8 +129,7 @@
+ android:label="HelpFragment" />
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
- */
-package org.kiwix.kiwixmobile.core.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) :
- RecyclerView.Adapter() {
- 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(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)
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpFragment.kt
index 8a059b9dd..8fdca13f8 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpFragment.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpFragment.kt
@@ -17,20 +17,16 @@
*/
package org.kiwix.kiwixmobile.core.help
+import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.appcompat.app.AppCompatActivity
-import androidx.appcompat.widget.Toolbar
-import androidx.recyclerview.widget.DividerItemDecoration
-import org.kiwix.kiwixmobile.core.R
+import androidx.compose.ui.platform.ComposeView
import org.kiwix.kiwixmobile.core.base.BaseActivity
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.ui.components.NavigationIcon
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import javax.inject.Inject
@@ -38,64 +34,47 @@ import javax.inject.Inject
abstract class HelpFragment : BaseFragment() {
@Inject
lateinit var sharedPreferenceUtil: SharedPreferenceUtil
- private var fragmentHelpBinding: FragmentHelpBinding? = null
+
+ // Each subclass is responsible for providing its own raw data.
protected open fun rawTitleDescriptionMap(): List> = 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) {
(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(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
- ): View? {
- fragmentHelpBinding =
- FragmentHelpBinding.inflate(inflater, container, false)
- return fragmentHelpBinding?.root
- }
-
- private fun sendDiagnosticReport() {
- requireActivity().start()
- }
-
- override fun onDestroyView() {
- super.onDestroyView()
- fragmentHelpBinding?.root?.removeAllViews()
- fragmentHelpBinding = null
+ ): View? = ComposeView(requireContext()).apply {
+ setContent {
+ // Create the helpScreen data using your rawTitleDescriptionMap.
+ val helpScreenData = transformToHelpScreenData(
+ requireContext(),
+ rawTitleDescriptionMap()
+ )
+ // Call your HelpScreen composable.
+ HelpScreen(data = helpScreenData) {
+ NavigationIcon(onClick = { activity?.onBackPressedDispatcher?.onBackPressed() })
+ }
+ }
+ }
+}
+
+// Util function to modify the data accordingly
+fun transformToHelpScreenData(
+ context: Context,
+ rawTitleDescriptionMap: List>
+): List {
+ 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)
}
}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt
new file mode 100644
index 000000000..0b06bde81
--- /dev/null
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt
@@ -0,0 +1,124 @@
+/*
+ * Kiwix Android
+ * Copyright (c) 2025 Kiwix
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.kiwix.kiwixmobile.core.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,
+ 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() }
+ .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, dividerColor: Color) {
+ LazyColumn(modifier = Modifier.fillMaxWidth()) {
+ itemsIndexed(data, key = { _, item -> item.title }) { _, item ->
+ HelpScreenItem(data = item)
+ HorizontalDivider(color = dividerColor, thickness = HELP_SCREEN_DIVIDER_HEIGHT)
+ }
+ }
+}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt
new file mode 100644
index 000000000..d2f921307
--- /dev/null
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt
@@ -0,0 +1,178 @@
+/*
+ * Kiwix Android
+ * Copyright (c) 2025 Kiwix
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.kiwix.kiwixmobile.core.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()
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItemDataClass.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItemDataClass.kt
new file mode 100644
index 000000000..9b4dd1358
--- /dev/null
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItemDataClass.kt
@@ -0,0 +1,22 @@
+/*
+ * Kiwix Android
+ * Copyright (c) 2025 Kiwix
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package org.kiwix.kiwixmobile.core.help
+
+// Same as HelpItem data class in earlier in XML
+data class HelpScreenItemDataClass(val title: String, val description: String)
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CoreSettingsFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CoreSettingsFragment.kt
index 579499e3f..6d258ec80 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CoreSettingsFragment.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CoreSettingsFragment.kt
@@ -59,6 +59,7 @@ abstract class CoreSettingsFragment : BaseFragment() {
requireActivity().supportFragmentManager.beginTransaction().remove(prefsFragment)
.commitNowAllowingStateLoss()
super.onDestroyView()
+ settingsBinding?.root?.removeAllViews()
settingsBinding = null
}
}
diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt
index c4ac32a1f..d597ba518 100644
--- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt
+++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt
@@ -98,4 +98,10 @@ object ComposeDimens {
// LocalLibraryFragment dimens
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
}
diff --git a/core/src/main/res/layout/fragment_help.xml b/core/src/main/res/layout/fragment_help.xml
deleted file mode 100644
index e25e528db..000000000
--- a/core/src/main/res/layout/fragment_help.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/src/main/res/layout/fragment_page.xml b/core/src/main/res/layout/fragment_page.xml
index d4e449bd2..2fb96ca8a 100644
--- a/core/src/main/res/layout/fragment_page.xml
+++ b/core/src/main/res/layout/fragment_page.xml
@@ -9,8 +9,7 @@
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:layout_constraintTop_toTopOf="parent"
- tools:showIn="@layout/fragment_help">
+ app:layout_constraintTop_toTopOf="parent">
-
-
-
-
-
-
-
-
diff --git a/core/src/main/res/layout/layout_standard_app_bar.xml b/core/src/main/res/layout/layout_standard_app_bar.xml
index 15e881b34..9cef6de5a 100644
--- a/core/src/main/res/layout/layout_standard_app_bar.xml
+++ b/core/src/main/res/layout/layout_standard_app_bar.xml
@@ -6,8 +6,7 @@
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:layout_constraintTop_toTopOf="parent"
- tools:showIn="@layout/fragment_help">
+ app:layout_constraintTop_toTopOf="parent">