From 00330273d2c22f65381b392d62052348eac93d05 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 26 Mar 2025 18:39:25 +0530 Subject: [PATCH 1/8] kiwix#4159 migrated Help Fragment to jetpack Compose --- .../kiwixmobile/help/KiwixHelpFragment.kt | 3 + core/build.gradle.kts | 21 +++ .../kiwixmobile/core/help/HelpFragment.kt | 98 +++++------ .../kiwix/kiwixmobile/core/help/HelpScreen.kt | 152 ++++++++++++++++++ .../kiwixmobile/core/help/HelpScreenItem.kt | 125 ++++++++++++++ .../core/help/HelpScreenItemDataClass.kt | 22 +++ 6 files changed, 375 insertions(+), 46 deletions(-) create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItemDataClass.kt diff --git a/app/src/main/java/org/kiwix/kiwixmobile/help/KiwixHelpFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/help/KiwixHelpFragment.kt index c033dd178..e1477a46e 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/help/KiwixHelpFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/help/KiwixHelpFragment.kt @@ -22,6 +22,9 @@ import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.help.HelpFragment class KiwixHelpFragment : HelpFragment() { + override val navHostFragmentId: Int + get() = org.kiwix.kiwixmobile.R.id.nav_host_fragment + override fun rawTitleDescriptionMap() = if (sharedPreferenceUtil.isPlayStoreBuildWithAndroid11OrAbove()) { listOf( diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 20216805f..1846f1785 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -13,6 +13,8 @@ buildscript { } plugins { `android-library` + id("org.jetbrains.kotlin.android") + id("org.jetbrains.kotlin.plugin.compose") version Versions.org_jetbrains_kotlin_plugin_compose } plugins.apply(KiwixConfigurationPlugin::class) apply(plugin = "io.objectbox") @@ -26,6 +28,12 @@ android { isMinifyEnabled = false } } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = Versions.kotlin_compiler_extension_version + } } fun shouldUseLocalVersion() = File(projectDir, "libs").exists() @@ -63,4 +71,17 @@ dependencies { implementation(Libs.kotlinx_coroutines_android) implementation(Libs.kotlinx_coroutines_rx3) implementation(Libs.zxing) + + implementation(Libs.androidx_compose_material3) + implementation(Libs.androidx_activity_compose) + + implementation(Libs.androidx_compose_ui) + implementation(platform(Libs.androidx_compose_bom)) + implementation(Libs.androidx_compose_ui_tooling) + implementation(Libs.androidx_compose_runtime_livedata) + implementation(Libs.androidx_compose_runtime_rxjava2) + + // For Compose UI Testing + androidTestImplementation(Libs.androidx_compose_ui_test_junit4) + debugImplementation(Libs.androidx_compose_ui_tooling) } 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..30010f581 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,19 +17,18 @@ */ 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 android.widget.Toolbar import androidx.appcompat.widget.Toolbar -import androidx.recyclerview.widget.DividerItemDecoration +import androidx.compose.ui.platform.ComposeView +import androidx.navigation.Navigation import org.kiwix.kiwixmobile.core.R 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.utils.SharedPreferenceUtil import javax.inject.Inject @@ -38,64 +37,71 @@ import javax.inject.Inject abstract class HelpFragment : BaseFragment() { @Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil - private var fragmentHelpBinding: FragmentHelpBinding? = null - 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") - } - } + protected abstract val navHostFragmentId: Int - getString(title) to descriptionValue + // Instead of keeping the XML binding, we now directly return a ComposeView. + protected open fun createFragmentView( + inflater: LayoutInflater, + container: ViewGroup? + ): View { + return ComposeView(requireContext()).apply { + setContent { + // Create the helpScreen data using your rawTitleDescriptionMap. + val helpScreenData = transformToHelpScreenData( + requireContext(), + rawTitleDescriptionMap() + ) + // Retrieve the NavController if your composable needs it. + val navController = Navigation.findNavController(requireActivity(), navHostFragmentId) + // Call your HelpScreen composable. + HelpScreen(data = helpScreenData, navController = navController) + } } } + // Each subclass is responsible for providing its own raw data. + protected open fun rawTitleDescriptionMap(): List> = emptyList() + + // The following properties are now optional – if no longer use an XML toolbar or title, + // we can remove or update these accordingly. + override val fragmentToolbar: Toolbar? by lazy { + // Already Applied ad TopAppBAr in scaffold in composable + null + } + override val fragmentTitle: String? by lazy { getString(R.string.menu_help) } + override fun inject(baseActivity: BaseActivity) { (baseActivity as CoreMainActivity).cachedComponent.inject(this) } + // Remove or adjust onViewCreated if you no longer need to manipulate XML-based views. 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) + // Any additional logic that is independent of the XML layout can be kept here. } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - fragmentHelpBinding = - FragmentHelpBinding.inflate(inflater, container, false) - return fragmentHelpBinding?.root - } + ): View? = createFragmentView(inflater, container) +} - private fun sendDiagnosticReport() { - requireActivity().start() - } - - override fun onDestroyView() { - super.onDestroyView() - fragmentHelpBinding?.root?.removeAllViews() - fragmentHelpBinding = null +// 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..2cba40c59 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt @@ -0,0 +1,152 @@ +/* + * 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.fillMaxSize +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.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults +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.res.colorResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +import androidx.navigation.NavController +import org.kiwix.kiwixmobile.core.R +import org.kiwix.kiwixmobile.core.error.DiagnosticReportActivity +import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HelpScreen( + modifier: Modifier = Modifier, + data: List, + navController: NavController +) { + val context = LocalContext.current + + val isDarkTheme = isSystemInDarkTheme() + + val backgroundColor = + if (isDarkTheme) colorResource(id = R.color.mine_shaft_gray900) else Color.White + val dividerColor = + if (isDarkTheme) colorResource(id = R.color.mine_shaft_gray600) else colorResource( + id = R.color.mine_shaft_gray350 + ) + + Scaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + TopAppBar( + title = { + Text( + modifier = modifier.padding(start = 16.dp), + text = stringResource(id = R.string.menu_help), + color = Color.White // Set title text color to white + ) + }, + navigationIcon = { + IconButton(onClick = navController::popBackStack) { + Icon( + imageVector = Icons.Filled.ArrowBack, + contentDescription = "Back", + tint = Color.White // Set navigation icon color to white + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.Black // Set top app bar background color to black + ) + ) + }, + containerColor = backgroundColor + ) { + + Column( + modifier = Modifier + .padding(it) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + (context as? Activity)?.start() + }, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start + ) { + Image( + painter = painterResource(R.drawable.ic_feedback_orange_24dp), + contentDescription = "Feedback", + modifier = Modifier + .padding(16.dp) + ) + + Text( + text = stringResource(R.string.send_report), + color = if (isDarkTheme) Color.LightGray else Color.DarkGray, + fontSize = 18.sp + ) + } + + LazyColumn( + modifier = Modifier + .fillMaxWidth() + ) { + itemsIndexed(data, key = { _, item -> item.title }) { index, item -> + HorizontalDivider( + color = dividerColor + ) + HelpScreenItem(data = item) + } + item { + HorizontalDivider( + color = dividerColor + ) + } + } + } + } +} 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..e9a90cdb1 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt @@ -0,0 +1,125 @@ +/* + * 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 androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.tween +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.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.KeyboardArrowDown +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +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.graphicsLayer +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun HelpScreenItem( + modifier: Modifier = Modifier, + data: HelpScreenItemDataClass, + initiallyOpened: Boolean = false +) { + var isOpen by remember { mutableStateOf(initiallyOpened) } + val isDarkTheme = isSystemInDarkTheme() + val itemColor = if (isDarkTheme) Color.White else Color.Black + val arrowRotation by animateFloatAsState( + targetValue = if (isOpen) 180f else 0f, + animationSpec = tween(300), + label = "arrowRotation" + ) + + val interactionSource = remember(::MutableInteractionSource) + + Column( + modifier = modifier + .fillMaxWidth() + .padding(top = 12.dp), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clickable(interactionSource = interactionSource, indication = null, onClick = { + isOpen = !isOpen + }) + .padding(horizontal = 16.dp) + ) { + Text( + text = data.title, + fontSize = 18.sp, + color = itemColor, + fontWeight = FontWeight.SemiBold + ) + Icon( + imageVector = Icons.Default.KeyboardArrowDown, + contentDescription = "Open or Close DropDown", + modifier = Modifier + .graphicsLayer { + rotationZ = arrowRotation + } + .size(46.dp), + tint = itemColor + ) + } + + Spacer(modifier = Modifier.height(12.dp)) + + AnimatedVisibility(visible = isOpen) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .padding(start = 16.dp, end = 16.dp) + ) { + Text( + text = data.description, + fontSize = 16.sp, + textAlign = TextAlign.Left, + color = itemColor, + modifier = Modifier.padding(bottom = 8.dp) + ) + } + } + } +} 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..6e6fb74af --- /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 HelpAdapter.kt +data class HelpScreenItemDataClass(val title: String, val description: String) From 55b6ab129d5a93f785962023fb17277857d853f8 Mon Sep 17 00:00:00 2001 From: Soumen Pal Date: Fri, 7 Mar 2025 23:02:09 +0530 Subject: [PATCH 2/8] Fixed Linted issuse related to Magic numbers and long methods and did the requested changes --- .../kiwixmobile/core/help/HelpAdapter.kt | 74 ------- .../kiwix/kiwixmobile/core/help/HelpScreen.kt | 185 +++++++++++------- .../kiwixmobile/core/help/HelpScreenItem.kt | 135 ++++++++----- .../core/help/HelpScreenItemDataClass.kt | 2 +- .../custom/help/CustomHelpFragment.kt | 30 ++- lintConfig.xml | 1 + 6 files changed, 231 insertions(+), 196 deletions(-) delete mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpAdapter.kt diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpAdapter.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpAdapter.kt deleted file mode 100644 index 0051deff0..000000000 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpAdapter.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2019 Kiwix - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -package org.kiwix.kiwixmobile.core.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/HelpScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt index 2cba40c59..5efb97b49 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt @@ -25,14 +25,16 @@ 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.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon @@ -47,10 +49,12 @@ 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.LocalDensity import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp import androidx.navigation.NavController @@ -58,95 +62,144 @@ import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.error.DiagnosticReportActivity import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.start -@OptIn(ExperimentalMaterial3Api::class) +val SendDiagnosticReportFontSize = 18.sp + @Composable fun HelpScreen( modifier: Modifier = Modifier, data: List, navController: NavController ) { - val context = LocalContext.current - val isDarkTheme = isSystemInDarkTheme() - val backgroundColor = if (isDarkTheme) colorResource(id = R.color.mine_shaft_gray900) else Color.White val dividerColor = - if (isDarkTheme) colorResource(id = R.color.mine_shaft_gray600) else colorResource( - id = R.color.mine_shaft_gray350 - ) + if (isDarkTheme) colorResource(id = R.color.mine_shaft_gray600) + else colorResource(id = R.color.mine_shaft_gray350) Scaffold( modifier = Modifier.fillMaxSize(), topBar = { - TopAppBar( - title = { - Text( - modifier = modifier.padding(start = 16.dp), - text = stringResource(id = R.string.menu_help), - color = Color.White // Set title text color to white - ) - }, - navigationIcon = { - IconButton(onClick = navController::popBackStack) { - Icon( - imageVector = Icons.Filled.ArrowBack, - contentDescription = "Back", - tint = Color.White // Set navigation icon color to white - ) - } - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = Color.Black // Set top app bar background color to black - ) - ) + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + HelpTopAppBar(navController) + } }, containerColor = backgroundColor - ) { + ) { innerPadding -> + HelpContent(data, dividerColor, innerPadding) + } +} - Column( - modifier = Modifier - .padding(it) - ) { +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun HelpTopAppBar(navController: NavController) { + // Retrieve the actionBarSize from the current theme + val context = LocalContext.current + val actionBarHeight = with(LocalDensity.current) { + // Obtain the height defined in the theme (usually 56dp on phones) + val styledAttributes = + context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.actionBarSize)) + styledAttributes.getDimension(0, 0f).toDp().also { styledAttributes.recycle() } + } + + TopAppBar( + modifier = Modifier.height(actionBarHeight), // set the height here + title = { Row( - modifier = Modifier - .fillMaxWidth() - .clickable { - (context as? Activity)?.start() - }, - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start + modifier = Modifier.fillMaxSize(), + verticalAlignment = Alignment.CenterVertically ) { - Image( - painter = painterResource(R.drawable.ic_feedback_orange_24dp), - contentDescription = "Feedback", - modifier = Modifier - .padding(16.dp) - ) - Text( - text = stringResource(R.string.send_report), - color = if (isDarkTheme) Color.LightGray else Color.DarkGray, - fontSize = 18.sp + modifier = Modifier.padding( + start = dimensionResource(R.dimen.activity_horizontal_margin) + ), + text = stringResource(id = R.string.menu_help), + color = Color.White, + fontWeight = FontWeight.SemiBold ) } - - LazyColumn( - modifier = Modifier - .fillMaxWidth() - ) { - itemsIndexed(data, key = { _, item -> item.title }) { index, item -> - HorizontalDivider( - color = dividerColor - ) - HelpScreenItem(data = item) - } - item { - HorizontalDivider( - color = dividerColor + }, + navigationIcon = { + Row(modifier = Modifier.fillMaxHeight(), verticalAlignment = Alignment.CenterVertically) { + IconButton(onClick = navController::popBackStack) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back_Navigation", + tint = Color.White ) } } + + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.Black + ) + ) +} + +@Composable +fun HelpContent( + data: List, + dividerColor: Color, + innerPadding: androidx.compose.foundation.layout.PaddingValues +) { + Column( + modifier = Modifier + .padding(innerPadding) + ) { + SendReportRow() + HelpItemList(data, dividerColor) + } +} + +@Composable +fun SendReportRow() { + val context = LocalContext.current + val isDarkTheme = isSystemInDarkTheme() + + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + (context as? Activity)?.start() + }, + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start + ) { + Image( + painter = painterResource(R.drawable.ic_feedback_orange_24dp), + contentDescription = stringResource(R.string.send_report), + modifier = Modifier + .padding(dimensionResource(R.dimen.activity_horizontal_margin)) + ) + + Text( + text = stringResource(R.string.send_report), + color = if (isDarkTheme) Color.LightGray else Color.DarkGray, + fontSize = SendDiagnosticReportFontSize + ) + } +} + +@Composable +fun HelpItemList(data: List, dividerColor: Color) { + LazyColumn( + modifier = Modifier + .fillMaxWidth() + ) { + itemsIndexed(data, key = { _, item -> item.title }) { _, item -> + HorizontalDivider( + color = dividerColor + ) + HelpScreenItem(data = item) + } + item { + HorizontalDivider( + color = dividerColor + ) } } } 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 index e9a90cdb1..a15ab7847 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt @@ -46,10 +46,23 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import org.kiwix.kiwixmobile.core.R + +// Define constants for spacing, font sizes, etc. + +private val HelpItemTitleFontSize = 22.sp +private val HelpItemDescriptionFontSize = 17.sp +private val IconSize = 36.dp +private const val HelpItemAnimationDuration = 300 +private const val HelpItemArrowRotationOpen = 180f +private const val HelpItemArrowRotationClosed = 0f @Composable fun HelpScreenItem( @@ -58,68 +71,82 @@ fun HelpScreenItem( initiallyOpened: Boolean = false ) { var isOpen by remember { mutableStateOf(initiallyOpened) } - val isDarkTheme = isSystemInDarkTheme() - val itemColor = if (isDarkTheme) Color.White else Color.Black - val arrowRotation by animateFloatAsState( - targetValue = if (isOpen) 180f else 0f, - animationSpec = tween(300), - label = "arrowRotation" - ) + val itemColor = if (isSystemInDarkTheme()) Color.White else Color.Black - val interactionSource = remember(::MutableInteractionSource) + val topPadding: Dp = dimensionResource(id = R.dimen.dimen_medium_padding) + val horizontalPadding: Dp = dimensionResource(id = R.dimen.activity_horizontal_margin) Column( modifier = modifier - .fillMaxWidth() - .padding(top = 12.dp), + .fillMaxWidth(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - Row( - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier - .fillMaxWidth() - .clickable(interactionSource = interactionSource, indication = null, onClick = { - isOpen = !isOpen - }) - .padding(horizontal = 16.dp) - ) { - Text( - text = data.title, - fontSize = 18.sp, - color = itemColor, - fontWeight = FontWeight.SemiBold - ) - Icon( - imageVector = Icons.Default.KeyboardArrowDown, - contentDescription = "Open or Close DropDown", - modifier = Modifier - .graphicsLayer { - rotationZ = arrowRotation - } - .size(46.dp), - tint = itemColor - ) - } - - Spacer(modifier = Modifier.height(12.dp)) - + HelpItemHeader(data.title, isOpen, itemColor, horizontalPadding) { isOpen = !isOpen } AnimatedVisibility(visible = isOpen) { - Box( - contentAlignment = Alignment.Center, - modifier = Modifier - .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp) - ) { - Text( - text = data.description, - fontSize = 16.sp, - textAlign = TextAlign.Left, - color = itemColor, - modifier = Modifier.padding(bottom = 8.dp) - ) - } + Spacer(modifier = Modifier.height(topPadding)) + HelpItemDescription(data.description, itemColor, horizontalPadding) } } } + +@Composable +fun HelpItemHeader( + title: String, + isOpen: Boolean, + itemColor: Color, + horizontalPadding: Dp, + onToggle: () -> Unit +) { + val arrowRotation by animateFloatAsState( + targetValue = if (isOpen) HelpItemArrowRotationOpen else HelpItemArrowRotationClosed, + animationSpec = tween(HelpItemAnimationDuration), + label = "arrowRotation" + ) + val interactionSource = remember(::MutableInteractionSource) + + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clickable(interactionSource = interactionSource, indication = null, onClick = onToggle) + .padding(horizontal = horizontalPadding, vertical = horizontalPadding) + ) { + Text( + text = title, + fontSize = HelpItemTitleFontSize, + color = itemColor, + fontWeight = FontWeight.Normal + ) + Icon( + imageVector = Icons.Default.KeyboardArrowDown, + contentDescription = stringResource(R.string.expand), + modifier = Modifier + .graphicsLayer { + rotationZ = arrowRotation + } + .size(IconSize), + tint = itemColor + ) + } +} + +@Composable +fun HelpItemDescription(description: String, itemColor: Color, horizontalPadding: Dp) { + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .fillMaxWidth() + .padding(start = horizontalPadding, end = horizontalPadding) + ) { + Text( + text = description, + fontSize = HelpItemDescriptionFontSize, + textAlign = TextAlign.Left, + color = itemColor, + modifier = Modifier.padding(bottom = horizontalPadding), + fontWeight = FontWeight.Normal + ) + } +} 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 index 6e6fb74af..083d4238e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItemDataClass.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItemDataClass.kt @@ -18,5 +18,5 @@ package org.kiwix.kiwixmobile.core.help -//same as HelpItem data class in HelpAdapter.kt +// same as HelpItem data class in earlier in XML data class HelpScreenItemDataClass(val title: String, val description: String) diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/help/CustomHelpFragment.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/help/CustomHelpFragment.kt index 32c898824..b24cad1a4 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/help/CustomHelpFragment.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/help/CustomHelpFragment.kt @@ -20,4 +20,32 @@ package org.kiwix.kiwixmobile.custom.help import org.kiwix.kiwixmobile.core.help.HelpFragment -class CustomHelpFragment : HelpFragment() +class CustomHelpFragment : HelpFragment() { + override val navHostFragmentId: Int + get() = org.kiwix.kiwixmobile.custom.R.id.custom_nav_controller + + override fun rawTitleDescriptionMap() = + if (sharedPreferenceUtil.isPlayStoreBuildWithAndroid11OrAbove()) { + listOf( + org.kiwix.kiwixmobile.core.R.string.help_2 to + org.kiwix.kiwixmobile.core.R.array.description_help_2, + org.kiwix.kiwixmobile.core.R.string.help_5 to + org.kiwix.kiwixmobile.core.R.array.description_help_5, + org.kiwix.kiwixmobile.core.R.string.how_to_update_content to + org.kiwix.kiwixmobile.core.R.array.update_content_description, + org.kiwix.kiwixmobile.core.R.string.why_copy_move_files_to_app_directory to + getString( + org.kiwix.kiwixmobile.core.R.string.copy_move_files_to_app_directory_description + ) + ) + } else { + listOf( + org.kiwix.kiwixmobile.core.R.string.help_2 to + org.kiwix.kiwixmobile.core.R.array.description_help_2, + org.kiwix.kiwixmobile.core.R.string.help_5 to + org.kiwix.kiwixmobile.core.R.array.description_help_5, + org.kiwix.kiwixmobile.core.R.string.how_to_update_content to + org.kiwix.kiwixmobile.core.R.array.update_content_description + ) + } +} diff --git a/lintConfig.xml b/lintConfig.xml index 44af8acfc..5ef86f071 100644 --- a/lintConfig.xml +++ b/lintConfig.xml @@ -1,5 +1,6 @@ + From 64db5ad3e5a9e2dda6c87349ab0567b184ed7ed6 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Tue, 25 Mar 2025 16:04:54 +0530 Subject: [PATCH 3/8] Removed the unnecessary compose dependencies from project. * Removed the unnecessary overriding of rows since we are not showing these rows in custom apps. * Used `KiwixTheme` instead of hardcoded colors. * Used the `KiwixAppBar` instead of manually created `AppBar`. * Refined the `HelpScreen` for better readability and maintainability. --- .../kiwixmobile/help/KiwixHelpFragment.kt | 3 - .../kotlin/plugin/AllProjectConfigurer.kt | 1 - core/build.gradle.kts | 20 --- .../kiwixmobile/core/help/HelpFragment.kt | 55 ++----- .../kiwix/kiwixmobile/core/help/HelpScreen.kt | 146 ++++-------------- .../kiwixmobile/core/help/HelpScreenItem.kt | 10 +- .../core/help/HelpScreenItemDataClass.kt | 2 +- .../custom/help/CustomHelpFragment.kt | 30 +--- 8 files changed, 50 insertions(+), 217 deletions(-) diff --git a/app/src/main/java/org/kiwix/kiwixmobile/help/KiwixHelpFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/help/KiwixHelpFragment.kt index e1477a46e..c033dd178 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/help/KiwixHelpFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/help/KiwixHelpFragment.kt @@ -22,9 +22,6 @@ import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.help.HelpFragment class KiwixHelpFragment : HelpFragment() { - override val navHostFragmentId: Int - get() = org.kiwix.kiwixmobile.R.id.nav_host_fragment - override fun rawTitleDescriptionMap() = if (sharedPreferenceUtil.isPlayStoreBuildWithAndroid11OrAbove()) { listOf( diff --git a/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt b/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt index 0003daa21..6b8a1dbd3 100644 --- a/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt +++ b/buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt @@ -20,7 +20,6 @@ package plugin import Config import Libs -import Versions import com.android.build.api.dsl.CommonExtension import com.android.build.gradle.BaseExtension import io.gitlab.arturbosch.detekt.extensions.DetektExtension diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 1846f1785..228d5800b 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -14,7 +14,6 @@ buildscript { plugins { `android-library` id("org.jetbrains.kotlin.android") - id("org.jetbrains.kotlin.plugin.compose") version Versions.org_jetbrains_kotlin_plugin_compose } plugins.apply(KiwixConfigurationPlugin::class) apply(plugin = "io.objectbox") @@ -28,12 +27,6 @@ android { isMinifyEnabled = false } } - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = Versions.kotlin_compiler_extension_version - } } fun shouldUseLocalVersion() = File(projectDir, "libs").exists() @@ -71,17 +64,4 @@ dependencies { implementation(Libs.kotlinx_coroutines_android) implementation(Libs.kotlinx_coroutines_rx3) implementation(Libs.zxing) - - implementation(Libs.androidx_compose_material3) - implementation(Libs.androidx_activity_compose) - - implementation(Libs.androidx_compose_ui) - implementation(platform(Libs.androidx_compose_bom)) - implementation(Libs.androidx_compose_ui_tooling) - implementation(Libs.androidx_compose_runtime_livedata) - implementation(Libs.androidx_compose_runtime_rxjava2) - - // For Compose UI Testing - androidTestImplementation(Libs.androidx_compose_ui_test_junit4) - debugImplementation(Libs.androidx_compose_ui_tooling) } 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 30010f581..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 @@ -22,14 +22,11 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toolbar -import androidx.appcompat.widget.Toolbar import androidx.compose.ui.platform.ComposeView -import androidx.navigation.Navigation -import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.base.BaseActivity import org.kiwix.kiwixmobile.core.base.BaseFragment 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,54 +35,30 @@ abstract class HelpFragment : BaseFragment() { @Inject lateinit var sharedPreferenceUtil: SharedPreferenceUtil - protected abstract val navHostFragmentId: Int - - // Instead of keeping the XML binding, we now directly return a ComposeView. - protected open fun createFragmentView( - inflater: LayoutInflater, - container: ViewGroup? - ): View { - return ComposeView(requireContext()).apply { - setContent { - // Create the helpScreen data using your rawTitleDescriptionMap. - val helpScreenData = transformToHelpScreenData( - requireContext(), - rawTitleDescriptionMap() - ) - // Retrieve the NavController if your composable needs it. - val navController = Navigation.findNavController(requireActivity(), navHostFragmentId) - // Call your HelpScreen composable. - HelpScreen(data = helpScreenData, navController = navController) - } - } - } - // Each subclass is responsible for providing its own raw data. protected open fun rawTitleDescriptionMap(): List> = emptyList() - // The following properties are now optional – if no longer use an XML toolbar or title, - // we can remove or update these accordingly. - override val fragmentToolbar: Toolbar? by lazy { - // Already Applied ad TopAppBAr in scaffold in composable - null - } - override val fragmentTitle: String? by lazy { getString(R.string.menu_help) } - override fun inject(baseActivity: BaseActivity) { (baseActivity as CoreMainActivity).cachedComponent.inject(this) } - // Remove or adjust onViewCreated if you no longer need to manipulate XML-based views. - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - // Any additional logic that is independent of the XML layout can be kept here. - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? = createFragmentView(inflater, container) + ): 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 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 index 5efb97b49..a466338b1 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt @@ -25,147 +25,66 @@ 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.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowBack -import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar -import androidx.compose.material3.TopAppBarDefaults 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.LocalDensity -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -import androidx.navigation.NavController 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.SIXTEEN_DP -val SendDiagnosticReportFontSize = 18.sp - +@Suppress("ComposableLambdaParameterNaming") @Composable fun HelpScreen( - modifier: Modifier = Modifier, data: List, - navController: NavController + navigationIcon: @Composable () -> Unit ) { - val isDarkTheme = isSystemInDarkTheme() - val backgroundColor = - if (isDarkTheme) colorResource(id = R.color.mine_shaft_gray900) else Color.White val dividerColor = - if (isDarkTheme) colorResource(id = R.color.mine_shaft_gray600) - else colorResource(id = R.color.mine_shaft_gray350) - - Scaffold( - modifier = Modifier.fillMaxSize(), - topBar = { - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - HelpTopAppBar(navController) + if (isSystemInDarkTheme()) { + MineShaftGray600 + } else { + MineShaftGray350 + } + KiwixTheme { + Scaffold( + topBar = { + KiwixAppBar(R.string.menu_help, navigationIcon) } - }, - containerColor = backgroundColor - ) { innerPadding -> - HelpContent(data, dividerColor, innerPadding) - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun HelpTopAppBar(navController: NavController) { - // Retrieve the actionBarSize from the current theme - val context = LocalContext.current - val actionBarHeight = with(LocalDensity.current) { - // Obtain the height defined in the theme (usually 56dp on phones) - val styledAttributes = - context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.actionBarSize)) - styledAttributes.getDimension(0, 0f).toDp().also { styledAttributes.recycle() } - } - - TopAppBar( - modifier = Modifier.height(actionBarHeight), // set the height here - title = { - Row( - modifier = Modifier.fillMaxSize(), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - modifier = Modifier.padding( - start = dimensionResource(R.dimen.activity_horizontal_margin) - ), - text = stringResource(id = R.string.menu_help), - color = Color.White, - fontWeight = FontWeight.SemiBold - ) + ) { innerPadding -> + Column(modifier = Modifier.padding(innerPadding)) { + SendReportRow() + HorizontalDivider(color = dividerColor) + HelpItemList(data, dividerColor) } - }, - navigationIcon = { - Row(modifier = Modifier.fillMaxHeight(), verticalAlignment = Alignment.CenterVertically) { - IconButton(onClick = navController::popBackStack) { - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowBack, - contentDescription = "Back_Navigation", - tint = Color.White - ) - } - } - - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = Color.Black - ) - ) -} - -@Composable -fun HelpContent( - data: List, - dividerColor: Color, - innerPadding: androidx.compose.foundation.layout.PaddingValues -) { - Column( - modifier = Modifier - .padding(innerPadding) - ) { - SendReportRow() - HelpItemList(data, dividerColor) + } } } @Composable fun SendReportRow() { val context = LocalContext.current - val isDarkTheme = isSystemInDarkTheme() + // val isDarkTheme = isSystemInDarkTheme() Row( modifier = Modifier .fillMaxWidth() - .clickable { - (context as? Activity)?.start() - }, + .clickable { (context as? Activity)?.start() }, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -173,13 +92,13 @@ fun SendReportRow() { painter = painterResource(R.drawable.ic_feedback_orange_24dp), contentDescription = stringResource(R.string.send_report), modifier = Modifier - .padding(dimensionResource(R.dimen.activity_horizontal_margin)) + .padding(SIXTEEN_DP) ) Text( text = stringResource(R.string.send_report), - color = if (isDarkTheme) Color.LightGray else Color.DarkGray, - fontSize = SendDiagnosticReportFontSize + // color = if (isDarkTheme) Color.LightGray else Color.DarkGray, + style = MaterialTheme.typography.titleMedium ) } } @@ -191,15 +110,8 @@ fun HelpItemList(data: List, dividerColor: Color) { .fillMaxWidth() ) { itemsIndexed(data, key = { _, item -> item.title }) { _, item -> - HorizontalDivider( - color = dividerColor - ) HelpScreenItem(data = item) - } - item { - HorizontalDivider( - color = dividerColor - ) + HorizontalDivider(color = dividerColor) } } } 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 index a15ab7847..26fe6c08e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt @@ -60,9 +60,9 @@ import org.kiwix.kiwixmobile.core.R private val HelpItemTitleFontSize = 22.sp private val HelpItemDescriptionFontSize = 17.sp private val IconSize = 36.dp -private const val HelpItemAnimationDuration = 300 -private const val HelpItemArrowRotationOpen = 180f -private const val HelpItemArrowRotationClosed = 0f +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( @@ -99,8 +99,8 @@ fun HelpItemHeader( onToggle: () -> Unit ) { val arrowRotation by animateFloatAsState( - targetValue = if (isOpen) HelpItemArrowRotationOpen else HelpItemArrowRotationClosed, - animationSpec = tween(HelpItemAnimationDuration), + 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) 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 index 083d4238e..9b4dd1358 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItemDataClass.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItemDataClass.kt @@ -18,5 +18,5 @@ package org.kiwix.kiwixmobile.core.help -// same as HelpItem data class in earlier in XML +// Same as HelpItem data class in earlier in XML data class HelpScreenItemDataClass(val title: String, val description: String) diff --git a/custom/src/main/java/org/kiwix/kiwixmobile/custom/help/CustomHelpFragment.kt b/custom/src/main/java/org/kiwix/kiwixmobile/custom/help/CustomHelpFragment.kt index b24cad1a4..32c898824 100644 --- a/custom/src/main/java/org/kiwix/kiwixmobile/custom/help/CustomHelpFragment.kt +++ b/custom/src/main/java/org/kiwix/kiwixmobile/custom/help/CustomHelpFragment.kt @@ -20,32 +20,4 @@ package org.kiwix.kiwixmobile.custom.help import org.kiwix.kiwixmobile.core.help.HelpFragment -class CustomHelpFragment : HelpFragment() { - override val navHostFragmentId: Int - get() = org.kiwix.kiwixmobile.custom.R.id.custom_nav_controller - - override fun rawTitleDescriptionMap() = - if (sharedPreferenceUtil.isPlayStoreBuildWithAndroid11OrAbove()) { - listOf( - org.kiwix.kiwixmobile.core.R.string.help_2 to - org.kiwix.kiwixmobile.core.R.array.description_help_2, - org.kiwix.kiwixmobile.core.R.string.help_5 to - org.kiwix.kiwixmobile.core.R.array.description_help_5, - org.kiwix.kiwixmobile.core.R.string.how_to_update_content to - org.kiwix.kiwixmobile.core.R.array.update_content_description, - org.kiwix.kiwixmobile.core.R.string.why_copy_move_files_to_app_directory to - getString( - org.kiwix.kiwixmobile.core.R.string.copy_move_files_to_app_directory_description - ) - ) - } else { - listOf( - org.kiwix.kiwixmobile.core.R.string.help_2 to - org.kiwix.kiwixmobile.core.R.array.description_help_2, - org.kiwix.kiwixmobile.core.R.string.help_5 to - org.kiwix.kiwixmobile.core.R.array.description_help_5, - org.kiwix.kiwixmobile.core.R.string.how_to_update_content to - org.kiwix.kiwixmobile.core.R.array.update_content_description - ) - } -} +class CustomHelpFragment : HelpFragment() From 056b8097176bda7f6e0e01c5d3bcdca01391dc4f Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 26 Mar 2025 18:40:50 +0530 Subject: [PATCH 4/8] Improved the divider height on HelpScreen to match the previous design. --- .../kiwix/kiwixmobile/core/help/HelpScreen.kt | 52 ++++++++++++++++--- .../kiwixmobile/core/help/HelpScreenItem.kt | 34 ++++++------ .../kiwixmobile/core/utils/ComposeDimens.kt | 6 +++ 3 files changed, 70 insertions(+), 22 deletions(-) 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 index a466338b1..5904e6db0 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt @@ -19,6 +19,7 @@ package org.kiwix.kiwixmobile.core.help import android.app.Activity +import android.content.res.Configuration import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.isSystemInDarkTheme @@ -33,6 +34,7 @@ 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 @@ -40,13 +42,16 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview 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.components.NavigationIcon 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 @Suppress("ComposableLambdaParameterNaming") @@ -69,7 +74,7 @@ fun HelpScreen( ) { innerPadding -> Column(modifier = Modifier.padding(innerPadding)) { SendReportRow() - HorizontalDivider(color = dividerColor) + HorizontalDivider(color = dividerColor, thickness = HELP_SCREEN_DIVIDER_HEIGHT) HelpItemList(data, dividerColor) } } @@ -79,7 +84,7 @@ fun HelpScreen( @Composable fun SendReportRow() { val context = LocalContext.current - // val isDarkTheme = isSystemInDarkTheme() + val isDarkTheme = isSystemInDarkTheme() Row( modifier = Modifier @@ -91,14 +96,14 @@ fun SendReportRow() { Image( painter = painterResource(R.drawable.ic_feedback_orange_24dp), contentDescription = stringResource(R.string.send_report), - modifier = Modifier - .padding(SIXTEEN_DP) + modifier = Modifier.padding(SIXTEEN_DP) ) Text( text = stringResource(R.string.send_report), - // color = if (isDarkTheme) Color.LightGray else Color.DarkGray, - style = MaterialTheme.typography.titleMedium + color = if (isDarkTheme) Color.LightGray else Color.DarkGray, + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.minimumInteractiveComponentSize() ) } } @@ -111,7 +116,40 @@ fun HelpItemList(data: List, dividerColor: Color) { ) { itemsIndexed(data, key = { _, item -> item.title }) { _, item -> HelpScreenItem(data = item) - HorizontalDivider(color = dividerColor) + HorizontalDivider(color = dividerColor, thickness = HELP_SCREEN_DIVIDER_HEIGHT) } } } + +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +// @Preview() +@Composable +fun PreviewScreen() { + HelpScreen( + data = transformToHelpScreenData(LocalContext.current, rawTitleDescriptionMap()) + ) { NavigationIcon(onClick = { }) } +} + +fun rawTitleDescriptionMap(): List> = + listOf( + R.string.help_2 to R.array.description_help_2, + R.string.help_5 to R.array.description_help_5, + R.string.how_to_update_content to R.array.update_content_description + ) + +// 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/HelpScreenItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt index 26fe6c08e..1629cf3f2 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt @@ -21,6 +21,7 @@ package org.kiwix.kiwixmobile.core.help 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 @@ -32,11 +33,10 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.KeyboardArrowDown -import androidx.compose.material3.Icon 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 @@ -45,21 +45,23 @@ 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.layout.ContentScale import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import org.kiwix.kiwixmobile.core.R +import org.kiwix.kiwixmobile.core.utils.ComposeDimens.EIGHT_DP +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 // Define constants for spacing, font sizes, etc. -private val HelpItemTitleFontSize = 22.sp private val HelpItemDescriptionFontSize = 17.sp -private val IconSize = 36.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 @@ -72,19 +74,18 @@ fun HelpScreenItem( ) { var isOpen by remember { mutableStateOf(initiallyOpened) } val itemColor = if (isSystemInDarkTheme()) Color.White else Color.Black - - val topPadding: Dp = dimensionResource(id = R.dimen.dimen_medium_padding) val horizontalPadding: Dp = dimensionResource(id = R.dimen.activity_horizontal_margin) Column( modifier = modifier - .fillMaxWidth(), + .fillMaxWidth() + .padding(vertical = EIGHT_DP), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { HelpItemHeader(data.title, isOpen, itemColor, horizontalPadding) { isOpen = !isOpen } AnimatedVisibility(visible = isOpen) { - Spacer(modifier = Modifier.height(topPadding)) + Spacer(modifier = Modifier.height(EIGHT_DP)) HelpItemDescription(data.description, itemColor, horizontalPadding) } } @@ -111,23 +112,26 @@ fun HelpItemHeader( modifier = Modifier .fillMaxWidth() .clickable(interactionSource = interactionSource, indication = null, onClick = onToggle) - .padding(horizontal = horizontalPadding, vertical = horizontalPadding) + .padding(horizontal = horizontalPadding, vertical = EIGHT_DP) ) { Text( text = title, - fontSize = HelpItemTitleFontSize, + fontSize = HELP_SCREEN_ITEM_TITLE_TEXT_SIZE, color = itemColor, - fontWeight = FontWeight.Normal + fontWeight = FontWeight.Medium, + letterSpacing = HELP_SCREEN_ITEM_TITLE_LETTER_SPACING, + modifier = Modifier.minimumInteractiveComponentSize() ) - Icon( + Image( imageVector = Icons.Default.KeyboardArrowDown, contentDescription = stringResource(R.string.expand), modifier = Modifier .graphicsLayer { rotationZ = arrowRotation } - .size(IconSize), - tint = itemColor + .minimumInteractiveComponentSize(), + contentScale = ContentScale.Inside, + colorFilter = ColorFilter.tint(color = itemColor) ) } } 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..ca79aeca4 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_MINIMUM_SIZE = 48.dp } From 8e3a4c585bdfea9ea5cce79b25728c7b90d57f3a Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 26 Mar 2025 18:31:44 +0530 Subject: [PATCH 5/8] Fixed the design of the HelpScreen to match the previous one. * Fixed the arrow icon size. * Fixed the description size and color. Also, made it support web links as it did in the XML-based design. * Removed unused code from the project. --- .../main/res/navigation/kiwix_nav_graph.xml | 3 +- core/build.gradle.kts | 1 - .../kiwix/kiwixmobile/core/help/HelpScreen.kt | 55 ++------------ .../kiwixmobile/core/help/HelpScreenItem.kt | 76 +++++++++++-------- .../kiwixmobile/core/utils/ComposeDimens.kt | 2 +- core/src/main/res/layout/fragment_help.xml | 54 ------------- core/src/main/res/layout/fragment_page.xml | 3 +- core/src/main/res/layout/item_help.xml | 50 ------------ .../res/layout/layout_standard_app_bar.xml | 3 +- 9 files changed, 58 insertions(+), 189 deletions(-) delete mode 100644 core/src/main/res/layout/fragment_help.xml delete mode 100644 core/src/main/res/layout/item_help.xml 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" /> , navigationIcon: @Composable () -> Unit ) { - val dividerColor = - if (isSystemInDarkTheme()) { - MineShaftGray600 - } else { - MineShaftGray350 - } + val dividerColor = if (isSystemInDarkTheme()) { + MineShaftGray600 + } else { + MineShaftGray350 + } KiwixTheme { Scaffold( topBar = { @@ -102,7 +99,7 @@ fun SendReportRow() { Text( text = stringResource(R.string.send_report), color = if (isDarkTheme) Color.LightGray else Color.DarkGray, - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleMedium.copy(fontWeight = FontWeight.Normal), modifier = Modifier.minimumInteractiveComponentSize() ) } @@ -110,46 +107,10 @@ fun SendReportRow() { @Composable fun HelpItemList(data: List, dividerColor: Color) { - LazyColumn( - modifier = Modifier - .fillMaxWidth() - ) { + LazyColumn(modifier = Modifier.fillMaxWidth()) { itemsIndexed(data, key = { _, item -> item.title }) { _, item -> HelpScreenItem(data = item) HorizontalDivider(color = dividerColor, thickness = HELP_SCREEN_DIVIDER_HEIGHT) } } } - -@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) -// @Preview() -@Composable -fun PreviewScreen() { - HelpScreen( - data = transformToHelpScreenData(LocalContext.current, rawTitleDescriptionMap()) - ) { NavigationIcon(onClick = { }) } -} - -fun rawTitleDescriptionMap(): List> = - listOf( - R.string.help_2 to R.array.description_help_2, - R.string.help_5 to R.array.description_help_5, - R.string.how_to_update_content to R.array.update_content_description - ) - -// 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/HelpScreenItem.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt index 1629cf3f2..2e754499e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt @@ -18,6 +18,11 @@ 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 @@ -30,11 +35,13 @@ 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 @@ -47,21 +54,20 @@ 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.layout.ContentScale -import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.sp +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 -// Define constants for spacing, font sizes, etc. - -private val HelpItemDescriptionFontSize = 17.sp 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 @@ -73,20 +79,18 @@ fun HelpScreenItem( initiallyOpened: Boolean = false ) { var isOpen by remember { mutableStateOf(initiallyOpened) } - val itemColor = if (isSystemInDarkTheme()) Color.White else Color.Black - val horizontalPadding: Dp = dimensionResource(id = R.dimen.activity_horizontal_margin) Column( modifier = modifier .fillMaxWidth() - .padding(vertical = EIGHT_DP), + .padding(vertical = EIGHT_DP, horizontal = SIXTEEN_DP), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - HelpItemHeader(data.title, isOpen, itemColor, horizontalPadding) { isOpen = !isOpen } + HelpItemHeader(data.title, isOpen) { isOpen = !isOpen } AnimatedVisibility(visible = isOpen) { Spacer(modifier = Modifier.height(EIGHT_DP)) - HelpItemDescription(data.description, itemColor, horizontalPadding) + HelpItemDescription(LocalContext.current, data.description) } } } @@ -95,8 +99,6 @@ fun HelpScreenItem( fun HelpItemHeader( title: String, isOpen: Boolean, - itemColor: Color, - horizontalPadding: Dp, onToggle: () -> Unit ) { val arrowRotation by animateFloatAsState( @@ -112,13 +114,11 @@ fun HelpItemHeader( modifier = Modifier .fillMaxWidth() .clickable(interactionSource = interactionSource, indication = null, onClick = onToggle) - .padding(horizontal = horizontalPadding, vertical = EIGHT_DP) ) { Text( text = title, fontSize = HELP_SCREEN_ITEM_TITLE_TEXT_SIZE, - color = itemColor, - fontWeight = FontWeight.Medium, + style = MaterialTheme.typography.headlineMedium.copy(fontWeight = FontWeight.Medium), letterSpacing = HELP_SCREEN_ITEM_TITLE_LETTER_SPACING, modifier = Modifier.minimumInteractiveComponentSize() ) @@ -129,28 +129,44 @@ fun HelpItemHeader( .graphicsLayer { rotationZ = arrowRotation } + .defaultMinSize( + minWidth = HELP_SCREEN_ARROW_ICON_SIZE, + minHeight = HELP_SCREEN_ARROW_ICON_SIZE + ) .minimumInteractiveComponentSize(), - contentScale = ContentScale.Inside, - colorFilter = ColorFilter.tint(color = itemColor) + colorFilter = ColorFilter.tint(color = MaterialTheme.colorScheme.onSurface) ) } } @Composable -fun HelpItemDescription(description: String, itemColor: Color, horizontalPadding: Dp) { +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(start = horizontalPadding, end = horizontalPadding) + .padding(top = SIXTEEN_DP) ) { - Text( - text = description, - fontSize = HelpItemDescriptionFontSize, - textAlign = TextAlign.Left, - color = itemColor, - modifier = Modifier.padding(bottom = horizontalPadding), - fontWeight = FontWeight.Normal - ) + AndroidView( + factory = { helpItemDescription }, + modifier = Modifier.padding(bottom = SIXTEEN_DP) + ) { 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/utils/ComposeDimens.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/ComposeDimens.kt index ca79aeca4..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 @@ -103,5 +103,5 @@ object ComposeDimens { 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_MINIMUM_SIZE = 48.dp + 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"> From 4cdae0f6012c2f50414bfe1839de13b78a73cb55 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 26 Mar 2025 19:38:56 +0530 Subject: [PATCH 6/8] Refactored the `ErrorActivityTest` according to new compose UI. * Removed unnecessary rules from lintConfig file. * Refactored the `HelpFragmentTest` according to compose UI. --- .../kiwixmobile/error/ErrorActivityRobot.kt | 17 ++- .../kiwixmobile/error/ErrorActivityTest.kt | 8 +- .../kiwixmobile/help/HelpFragmentTest.kt | 57 +++++---- .../org/kiwix/kiwixmobile/help/HelpRobot.kt | 116 ++++++++++++------ .../kiwix/kiwixmobile/core/help/HelpScreen.kt | 10 +- .../kiwixmobile/core/help/HelpScreenItem.kt | 6 + lintConfig.xml | 1 - 7 files changed, 142 insertions(+), 73 deletions(-) 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..a9a07687c 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpRobot.kt @@ -17,16 +17,21 @@ */ 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.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.testutils.TestUtils.testFlakyView fun help(func: HelpRobot.() -> Unit) = HelpRobot().apply(func) @@ -36,61 +41,100 @@ class HelpRobot : BaseRobot() { isVisible(ViewId(id.toolbar)) } - 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/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt index 2ebbce795..0b06bde81 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreen.kt @@ -29,6 +29,7 @@ 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 @@ -39,6 +40,7 @@ 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 @@ -52,6 +54,11 @@ 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( @@ -86,7 +93,8 @@ fun SendReportRow() { Row( modifier = Modifier .fillMaxWidth() - .clickable { (context as? Activity)?.start() }, + .clickable { (context as? Activity)?.start() } + .testTag(SEND_DIAGNOSTIC_REPORT_TESTING_TAG), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { 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 index 2e754499e..d2f921307 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/help/HelpScreenItem.kt @@ -56,7 +56,10 @@ 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 @@ -114,6 +117,7 @@ fun HelpItemHeader( modifier = Modifier .fillMaxWidth() .clickable(interactionSource = interactionSource, indication = null, onClick = onToggle) + .testTag(HELP_SCREEN_ITEM_TITLE_TESTING_TAG) ) { Text( text = title, @@ -156,6 +160,8 @@ fun HelpItemDescription(context: Context, description: String) { AndroidView( factory = { helpItemDescription }, modifier = Modifier.padding(bottom = SIXTEEN_DP) + .testTag(HELP_SCREEN_ITEM_DESCRIPTION_TESTING_TAG) + .semantics { contentDescription = description } ) { textView -> textView.apply { text = description diff --git a/lintConfig.xml b/lintConfig.xml index 5ef86f071..44af8acfc 100644 --- a/lintConfig.xml +++ b/lintConfig.xml @@ -1,6 +1,5 @@ - From 1debc8269c0a7e8f72f56db76e191e274bcb95ab Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 26 Mar 2025 21:24:42 +0530 Subject: [PATCH 7/8] Fixed: `TopLevelDestinationTest`, and `GetContentShortcutTest` test cases which was failing due to migration to jetpack. --- .../java/org/kiwix/kiwixmobile/help/HelpRobot.kt | 12 ++++++++---- .../kiwixmobile/main/TopLevelDestinationTest.kt | 2 +- .../kiwixmobile/shortcuts/GetContentShortcutTest.kt | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) 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 a9a07687c..f1991e2c5 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/help/HelpRobot.kt @@ -27,18 +27,22 @@ import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.matcher.ViewMatchers.withText import org.kiwix.kiwixmobile.BaseRobot -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(composeTestRule: ComposeContentTestRule) { 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() } From bd5a9129bf5e67ecbd1ecb9ae833330b5a3a8f1f Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 26 Mar 2025 23:38:50 +0530 Subject: [PATCH 8/8] Fixed: Memory leak in `KiwixSettingFragment`. --- .../org/kiwix/kiwixmobile/core/settings/CoreSettingsFragment.kt | 1 + 1 file changed, 1 insertion(+) 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 } }