From 960189e68c22243a4acfdc90d2aae5878c5d241b Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Tue, 6 Aug 2024 16:25:55 +0530 Subject: [PATCH 1/7] Removed the dialog box to select the storage in settings. * Now, the storage selection (internal/external) is directly visible in the settings, allowing users to select storage without using a dialog box. * The total and available space for each storage option is displayed. * Additionally, the selected storage path is shown in the settings, so users know where the downloaded files will be saved. --- .../settings/KiwixPrefsFragment.kt | 79 +++++++++++++++---- .../core/settings/CorePrefsFragment.kt | 25 +----- .../settings/StorageRadioButtonPreference.kt | 49 ++++++++++++ .../core/utils/SharedPreferenceUtil.kt | 2 + .../src/main/res/layout/item_radio_button.xml | 26 ++++++ core/src/main/res/xml/preferences.xml | 15 ++-- 6 files changed, 154 insertions(+), 42 deletions(-) create mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageRadioButtonPreference.kt create mode 100644 core/src/main/res/layout/item_radio_button.xml diff --git a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt index 8eafc7d6cb..1a1dfcf487 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt @@ -21,20 +21,25 @@ package org.kiwix.kiwixmobile.settings import android.os.Build import android.os.Bundle import android.os.Environment -import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentActivity import androidx.preference.Preference import androidx.preference.PreferenceCategory -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext -import org.kiwix.kiwixmobile.R +import eu.mhutti1.utils.storage.StorageDevice +import eu.mhutti1.utils.storage.StorageDeviceUtils +import io.reactivex.Flowable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers import org.kiwix.kiwixmobile.core.navigateToSettings import org.kiwix.kiwixmobile.core.settings.CorePrefsFragment +import org.kiwix.kiwixmobile.core.settings.StorageRadioButtonPreference import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil -import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_STORAGE +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_EXTERNAL_STORAGE +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_INTERNAL_STORAGE class KiwixPrefsFragment : CorePrefsFragment() { + private var storageDisposable: Disposable? = null + private var storageDeviceList: List = listOf() override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { super.onCreatePreferences(savedInstanceState, rootKey) @@ -44,20 +49,57 @@ class KiwixPrefsFragment : CorePrefsFragment() { override fun setStorage() { sharedPreferenceUtil?.let { - val internalStorage = runBlocking { internalStorage() } - findPreference(PREF_STORAGE)?.title = getString( - if (it.prefStorage == internalStorage?.let( - it::getPublicDirectoryPath + if (storageDisposable?.isDisposed == false) { + // update the storage when user switch to other storage. + setUpStoragePreference() + } + storageDisposable = + Flowable.fromCallable { StorageDeviceUtils.getWritableStorage(requireActivity()) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { storageList -> + storageDeviceList = storageList + showExternalPreferenceIfAvailable() + setUpStoragePreference() + }, + Throwable::printStackTrace ) - ) R.string.internal_storage - else R.string.external_storage - ) } - findPreference(PREF_STORAGE)?.summary = storageCalculator?.calculateAvailableSpace() } - private suspend fun internalStorage(): String? = withContext(Dispatchers.IO) { - ContextCompat.getExternalFilesDirs(requireContext(), null).firstOrNull()?.path + private fun setUpStoragePreference() { + storageDeviceList.forEachIndexed { index, storageDevice -> + val storageSummary = buildStorageSummary(storageDevice, index) + val preferenceKey = if (index == 0) PREF_INTERNAL_STORAGE else PREF_EXTERNAL_STORAGE + val isChecked = sharedPreferenceUtil?.storagePosition == index + + findPreference(preferenceKey)?.apply { + summary = storageSummary + this.isChecked = isChecked + setOnPreferenceClickListener { + onStorageDeviceSelected(storageDevice) + true + } + } + } + } + + private fun buildStorageSummary(storageDevice: StorageDevice, index: Int): String { + val availableSpace = storageCalculator?.calculateAvailableSpace(storageDevice.file) + val totalSpace = storageCalculator?.calculateTotalSpace(storageDevice.file) + val storagePath = if (sharedPreferenceUtil?.storagePosition == index) { + "\n${sharedPreferenceUtil?.prefStorage}/Kiwix" + } else { + "" + } + + return "$availableSpace / $totalSpace $storagePath" + } + + private fun showExternalPreferenceIfAvailable() { + findPreference(PREF_EXTERNAL_STORAGE)?.isVisible = + storageDeviceList.size > 1 } private fun setMangeExternalStoragePermission() { @@ -87,6 +129,11 @@ class KiwixPrefsFragment : CorePrefsFragment() { preferenceCategory?.isVisible = true } + override fun onDestroyView() { + storageDisposable?.dispose() + super.onDestroyView() + } + companion object { const val PREF_MANAGE_EXTERNAL_STORAGE_PERMISSION = "pref_manage_external_storage" diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt index f24edc44c4..fd7184778a 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.kt @@ -40,7 +40,6 @@ import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import com.google.android.material.snackbar.Snackbar import eu.mhutti1.utils.storage.StorageDevice -import eu.mhutti1.utils.storage.StorageSelectDialog import org.kiwix.kiwixmobile.core.CoreApp.Companion.coreComponent import org.kiwix.kiwixmobile.core.CoreApp.Companion.instance import org.kiwix.kiwixmobile.core.DarkModeConfig @@ -301,9 +300,6 @@ abstract class CorePrefsFragment : if (preference.key.equals(PREF_CREDITS, ignoreCase = true)) { openCredits() } - if (preference.key.equals(SharedPreferenceUtil.PREF_STORAGE, ignoreCase = true)) { - openFolderSelect() - } if (preference.key.equals(PREF_EXPORT_BOOKMARK, ignoreCase = true) && requestExternalStorageWritePermissionForExportBookmark() ) { @@ -447,18 +443,8 @@ abstract class CorePrefsFragment : private fun isValidBookmarkFile(mimeType: String?) = mimeType == "application/xml" || mimeType == "text/xml" - private fun openFolderSelect() { - val dialogFragment = StorageSelectDialog() - dialogFragment.onSelectAction = - ::onStorageDeviceSelected - dialogFragment.show( - requireActivity().supportFragmentManager, - resources.getString(R.string.pref_storage) - ) - } - @Suppress("NestedBlockDepth") - private fun onStorageDeviceSelected(storageDevice: StorageDevice) { + fun onStorageDeviceSelected(storageDevice: StorageDevice) { sharedPreferenceUtil?.let { sharedPreferenceUtil -> findPreference(SharedPreferenceUtil.PREF_STORAGE)?.summary = storageCalculator?.calculateAvailableSpace(storageDevice.file) @@ -466,10 +452,9 @@ abstract class CorePrefsFragment : sharedPreferenceUtil.putPrefStorage( sharedPreferenceUtil.getPublicDirectoryPath(storageDevice.name) ) - findPreference(SharedPreferenceUtil.PREF_STORAGE)?.title = - getString(R.string.internal_storage) sharedPreferenceUtil.putStoragePosition(INTERNAL_SELECT_POSITION) setShowStorageOption() + setStorage() } else { if (sharedPreferenceUtil.isPlayStoreBuild) { setExternalStoragePath(storageDevice) @@ -492,10 +477,9 @@ abstract class CorePrefsFragment : private fun setExternalStoragePath(storageDevice: StorageDevice) { sharedPreferenceUtil?.putPrefStorage(storageDevice.name) - findPreference(SharedPreferenceUtil.PREF_STORAGE)?.title = - getString(R.string.external_storage) sharedPreferenceUtil?.putStoragePosition(EXTERNAL_SELECT_POSITION) setShowStorageOption() + setStorage() } private fun selectFolder() { @@ -516,10 +500,9 @@ abstract class CorePrefsFragment : result.data?.let { intent -> getPathFromUri(requireActivity(), intent)?.let { path -> sharedPreferenceUtil?.putPrefStorage(path) - findPreference(SharedPreferenceUtil.PREF_STORAGE)?.title = - getString(R.string.external_storage) sharedPreferenceUtil?.putStoragePosition(EXTERNAL_SELECT_POSITION) setShowStorageOption() + setStorage() } } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageRadioButtonPreference.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageRadioButtonPreference.kt new file mode 100644 index 0000000000..97d5ea31b0 --- /dev/null +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageRadioButtonPreference.kt @@ -0,0 +1,49 @@ +/* + * Kiwix Android + * Copyright (c) 2024 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.settings + +import android.content.Context +import android.util.AttributeSet +import androidx.preference.CheckBoxPreference +import org.kiwix.kiwixmobile.core.R + +class StorageRadioButtonPreference : CheckBoxPreference { + constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super( + context, + attrs, + defStyle + ) { + setView() + } + + constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) { + setView() + } + + private fun setView() { + widgetLayoutResource = R.layout.item_radio_button + } + + override fun onClick() { + if (this.isChecked) + return + + super.onClick() + } +} diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt index 8c4754a9aa..2c7c2d9c6e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt @@ -275,6 +275,8 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { const val PREF_LANG = "pref_language_chooser" const val PREF_DEVICE_DEFAULT_LANG = "pref_device_default_language" const val PREF_STORAGE = "pref_select_folder" + const val PREF_INTERNAL_STORAGE = "pref_internal_storage" + const val PREF_EXTERNAL_STORAGE = "pref_external_storage" const val STORAGE_POSITION = "storage_position" const val PREF_WIFI_ONLY = "pref_wifi_only" const val PREF_KIWIX_MOBILE = "kiwix-mobile" diff --git a/core/src/main/res/layout/item_radio_button.xml b/core/src/main/res/layout/item_radio_button.xml new file mode 100644 index 0000000000..98ac72d153 --- /dev/null +++ b/core/src/main/res/layout/item_radio_button.xml @@ -0,0 +1,26 @@ + + + + diff --git a/core/src/main/res/xml/preferences.xml b/core/src/main/res/xml/preferences.xml index 9d6df5a209..d57b7c248d 100644 --- a/core/src/main/res/xml/preferences.xml +++ b/core/src/main/res/xml/preferences.xml @@ -65,9 +65,14 @@ app:iconSpaceReserved="false" app:title="@string/pref_storage"> - + + @@ -118,8 +123,8 @@ + app:iconSpaceReserved="false" + app:isPreferenceVisible="false"> Date: Tue, 6 Aug 2024 18:17:48 +0530 Subject: [PATCH 2/7] Fixed KiwixSettingsFragmentTest was failing. --- .../settings/KiwixSettingsFragmentTest.kt | 5 ++--- .../kiwix/kiwixmobile/settings/SettingsRobot.kt | 15 +++++++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt index 18464a0f3d..e87d41a41a 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt @@ -101,9 +101,8 @@ class KiwixSettingsFragmentTest { toggleOpenNewTabInBackground() toggleExternalLinkWarningPref() toggleWifiDownloadsOnlyPref() - clickStoragePreference() - assertStorageDialogDisplayed() - dismissDialog() + clickExternalStoragePreference() + clickInternalStoragePreference() clickClearHistoryPreference() assertHistoryDialogDisplayed() dismissDialog() diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt index 3d6ba3211c..e69d3dcf54 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt @@ -18,6 +18,7 @@ package org.kiwix.kiwixmobile.settings +import android.os.Build import androidx.annotation.StringRes import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onData @@ -100,12 +101,18 @@ class SettingsRobot : BaseRobot() { isVisible(TextId(R.string.pref_language_title)) } - fun clickStoragePreference() { - clickRecyclerViewItems(R.string.internal_storage, R.string.external_storage) + fun clickInternalStoragePreference() { + clickRecyclerViewItems(R.string.internal_storage) } - fun assertStorageDialogDisplayed() { - isVisible(TextId(R.string.pref_storage)) + fun clickExternalStoragePreference() { + clickRecyclerViewItems(R.string.external_storage) + if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { + // for now when we will click on the external storage then it will show folder + // selection dialog which we are removing the #3935 so, for now, dismiss this dialog, we + // will remove this in #3935. + dismissDialog() + } } fun clickClearHistoryPreference() { From cbd35c036d18f23f4c9f984b9d9c73c1f6f7ee54 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 7 Aug 2024 12:06:36 +0530 Subject: [PATCH 3/7] Fixed ZimHostFragmentTest, and DeepLinksTest. --- .../java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt | 2 +- .../java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt index eceb7495e5..55990093a4 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/deeplinks/DeepLinksTest.kt @@ -105,7 +105,7 @@ class DeepLinksTest : BaseActivityTest() { private fun loadZimFileInApplicationAndReturnSchemeTypeUri(schemeType: String): Uri? { val loadFileStream = DeepLinksTest::class.java.classLoader.getResourceAsStream("testzim.zim") - val zimFile = File(sharedPreferenceUtil.prefStorage, "testzim.zim") + val zimFile = File(sharedPreferenceUtil.defaultStorage(), "testzim.zim") if (zimFile.exists()) zimFile.delete() zimFile.createNewFile() loadFileStream.use { inputStream -> diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt index 3ef94a59aa..1e24d4093a 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/webserver/ZimHostFragmentTest.kt @@ -199,7 +199,7 @@ class ZimHostFragmentTest { private fun loadZimFileInApplication(zimFileName: String) { val loadFileStream = ZimHostFragmentTest::class.java.classLoader.getResourceAsStream(zimFileName) - val zimFile = File(sharedPreferenceUtil.prefStorage, zimFileName) + val zimFile = File(sharedPreferenceUtil.defaultStorage(), zimFileName) if (zimFile.exists()) zimFile.delete() zimFile.createNewFile() loadFileStream.use { inputStream -> From 70245d3621249a080432b21f21c943300306518a Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 7 Aug 2024 13:04:02 +0530 Subject: [PATCH 4/7] Fixed: Sometimes internal storage is not found in KiwixSettngsFragmentTest --- .../java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt index e69d3dcf54..7278838986 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt @@ -18,7 +18,6 @@ package org.kiwix.kiwixmobile.settings -import android.os.Build import androidx.annotation.StringRes import androidx.recyclerview.widget.RecyclerView import androidx.test.espresso.Espresso.onData @@ -107,12 +106,6 @@ class SettingsRobot : BaseRobot() { fun clickExternalStoragePreference() { clickRecyclerViewItems(R.string.external_storage) - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R) { - // for now when we will click on the external storage then it will show folder - // selection dialog which we are removing the #3935 so, for now, dismiss this dialog, we - // will remove this in #3935. - dismissDialog() - } } fun clickClearHistoryPreference() { From 72fe21ba4c36072b9538480ab13495a51ee564bc Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 7 Aug 2024 17:32:37 +0530 Subject: [PATCH 5/7] Changed the visual representation of storage selection. * Now the storage path will show for non-selected storage as well. * Improved the KiwixSettingsFragmentTest. --- .../settings/KiwixSettingsFragmentTest.kt | 4 +- .../settings/KiwixPrefsFragment.kt | 72 ++++++++++++++---- .../core/settings/StorageCalculator.kt | 5 +- .../settings/StorageRadioButtonPreference.kt | 74 +++++++++++++++---- .../main/res/drawable/progress_bar_state.xml | 21 ++++++ .../src/main/res/layout/item_radio_button.xml | 26 ------- .../res/layout/item_storage_preference.xml | 65 ++++++++++++++++ core/src/main/res/values/strings.xml | 2 + core/src/main/res/xml/preferences.xml | 4 +- 9 files changed, 215 insertions(+), 58 deletions(-) create mode 100644 core/src/main/res/drawable/progress_bar_state.xml delete mode 100644 core/src/main/res/layout/item_radio_button.xml create mode 100644 core/src/main/res/layout/item_storage_preference.xml diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt index e87d41a41a..afaa28c01e 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/KiwixSettingsFragmentTest.kt @@ -79,7 +79,9 @@ class KiwixSettingsFragmentTest { handleLocaleChange( it, "en", - SharedPreferenceUtil(it) + SharedPreferenceUtil(it).apply { + setIsPlayStoreBuildType(true) + } ) it.navigate(R.id.introFragment) } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt index 1a1dfcf487..00f857c796 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt @@ -30,6 +30,7 @@ import io.reactivex.Flowable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers +import org.kiwix.kiwixmobile.core.R import org.kiwix.kiwixmobile.core.navigateToSettings import org.kiwix.kiwixmobile.core.settings.CorePrefsFragment import org.kiwix.kiwixmobile.core.settings.StorageRadioButtonPreference @@ -51,7 +52,7 @@ class KiwixPrefsFragment : CorePrefsFragment() { sharedPreferenceUtil?.let { if (storageDisposable?.isDisposed == false) { // update the storage when user switch to other storage. - setUpStoragePreference() + setUpStoragePreference(it) } storageDisposable = Flowable.fromCallable { StorageDeviceUtils.getWritableStorage(requireActivity()) } @@ -61,40 +62,85 @@ class KiwixPrefsFragment : CorePrefsFragment() { { storageList -> storageDeviceList = storageList showExternalPreferenceIfAvailable() - setUpStoragePreference() + setUpStoragePreference(it) }, Throwable::printStackTrace ) } } - private fun setUpStoragePreference() { + private fun setUpStoragePreference(sharedPreferenceUtil: SharedPreferenceUtil) { storageDeviceList.forEachIndexed { index, storageDevice -> - val storageSummary = buildStorageSummary(storageDevice, index) val preferenceKey = if (index == 0) PREF_INTERNAL_STORAGE else PREF_EXTERNAL_STORAGE - val isChecked = sharedPreferenceUtil?.storagePosition == index - + val selectedStoragePosition = sharedPreferenceUtil.storagePosition + val isChecked = selectedStoragePosition == index findPreference(preferenceKey)?.apply { - summary = storageSummary this.isChecked = isChecked setOnPreferenceClickListener { onStorageDeviceSelected(storageDevice) true } + setPathAndTitleForStorage( + buildStoragePathAndTitle( + storageDevice, + index, + selectedStoragePosition, + sharedPreferenceUtil + ) + ) + setFreeSpace(getFreeSpaceText(storageDevice)) + setUsedSpace(getUsedSpaceText(storageDevice)) + setProgress(calculateUsedPercentage(storageDevice)) } } } - private fun buildStorageSummary(storageDevice: StorageDevice, index: Int): String { - val availableSpace = storageCalculator?.calculateAvailableSpace(storageDevice.file) + private fun getFreeSpaceText(storageDevice: StorageDevice): String { + val freeSpace = storageCalculator?.calculateAvailableSpace(storageDevice.file) + return getString(R.string.pref_free_storage, freeSpace) + } + + private fun getUsedSpaceText(storageDevice: StorageDevice): String { + val usedSpace = storageCalculator?.calculateUsedSpace(storageDevice.file) + return getString(R.string.pref_storage_used, usedSpace) + } + + private fun buildStoragePathAndTitle( + storageDevice: StorageDevice, + index: Int, + selectedStoragePosition: Int, + sharedPreferenceUtil: SharedPreferenceUtil + ): String { + val storageName = if (storageDevice.isInternal) { + getString(R.string.internal_storage) + } else { + getString(R.string.external_storage) + } + val storagePath = if (index == selectedStoragePosition) { + sharedPreferenceUtil.prefStorage + } else { + getStoragePathForNonSelectedStorage(storageDevice, sharedPreferenceUtil) + } val totalSpace = storageCalculator?.calculateTotalSpace(storageDevice.file) - val storagePath = if (sharedPreferenceUtil?.storagePosition == index) { - "\n${sharedPreferenceUtil?.prefStorage}/Kiwix" + return "$storageName - $totalSpace\n$storagePath/Kiwix" + } + + private fun getStoragePathForNonSelectedStorage( + storageDevice: StorageDevice, + sharedPreferenceUtil: SharedPreferenceUtil + ): String = + if (storageDevice.isInternal) { + sharedPreferenceUtil.getPublicDirectoryPath(storageDevice.name) } else { - "" + storageDevice.name } - return "$availableSpace / $totalSpace $storagePath" + @Suppress("MagicNumber") + private fun calculateUsedPercentage(storageDevice: StorageDevice): Int { + val totalSpace = storageCalculator?.totalBytes(storageDevice.file) ?: 1 + val availableSpace = storageCalculator?.availableBytes(storageDevice.file) ?: 0 + val usedSpace = totalSpace - availableSpace + return (usedSpace.toDouble() / totalSpace * 100).toInt() } private fun showExternalPreferenceIfAvailable() { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageCalculator.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageCalculator.kt index e796bcbefb..8fffecdbef 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageCalculator.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageCalculator.kt @@ -36,9 +36,12 @@ class StorageCalculator @Inject constructor( fun calculateTotalSpace(file: File = File(sharedPreferenceUtil.prefStorage)): String = Bytes(totalBytes(file)).humanReadable + fun calculateUsedSpace(file: File): String = + Bytes(totalBytes(file) - availableBytes(file)).humanReadable + fun availableBytes(file: File = File(sharedPreferenceUtil.prefStorage)) = if (file.isFileExist()) file.freeSpace() else 0L - private fun totalBytes(file: File) = if (file.isFileExist()) file.totalSpace() else 0L + fun totalBytes(file: File) = if (file.isFileExist()) file.totalSpace() else 0L } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageRadioButtonPreference.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageRadioButtonPreference.kt index 97d5ea31b0..36d4532b73 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageRadioButtonPreference.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/StorageRadioButtonPreference.kt @@ -20,30 +20,74 @@ package org.kiwix.kiwixmobile.core.settings import android.content.Context import android.util.AttributeSet +import android.widget.ProgressBar +import android.widget.RadioButton +import android.widget.TextView import androidx.preference.CheckBoxPreference +import androidx.preference.PreferenceViewHolder import org.kiwix.kiwixmobile.core.R +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_EXTERNAL_STORAGE +import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.Companion.PREF_INTERNAL_STORAGE -class StorageRadioButtonPreference : CheckBoxPreference { - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super( - context, - attrs, - defStyle - ) { - setView() - } +class StorageRadioButtonPreference @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : CheckBoxPreference(context, attrs, defStyleAttr) { - constructor(context: Context, attrs: AttributeSet? = null) : super(context, attrs) { - setView() + init { + widgetLayoutResource = R.layout.item_storage_preference } - private fun setView() { - widgetLayoutResource = R.layout.item_radio_button + private var radioButton: RadioButton? = null + private var progressBar: ProgressBar? = null + private var usedSpaceTextView: TextView? = null + private var freeSpaceTextView: TextView? = null + private var pathAndTitleTextView: TextView? = null + private var usedSpace: String? = null + private var freeSpace: String? = null + private var pathAndTitle: String? = null + private var progress: Int = 0 + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + radioButton = holder.findViewById(R.id.radioButton) as RadioButton + progressBar = holder.findViewById(R.id.storageProgressBar) as ProgressBar + usedSpaceTextView = holder.findViewById(R.id.usedSpace) as TextView + freeSpaceTextView = holder.findViewById(R.id.freeSpace) as TextView + pathAndTitleTextView = holder.findViewById(R.id.storagePathAndTitle) as TextView + radioButton?.isChecked = isChecked + + usedSpaceTextView?.let { it.text = usedSpace } + freeSpaceTextView?.let { it.text = freeSpace } + pathAndTitleTextView?.let { it.text = pathAndTitle } + progressBar?.let { it.progress = progress } } override fun onClick() { - if (this.isChecked) - return - + if (isChecked) return + preferenceManager.findPreference(PREF_INTERNAL_STORAGE)?.isChecked = false + preferenceManager.findPreference(PREF_EXTERNAL_STORAGE)?.isChecked = false super.onClick() } + + fun setProgress(usedPercentage: Int) { + progress = usedPercentage + progressBar?.progress = usedPercentage + } + + fun setUsedSpace(usedSpace: String) { + this.usedSpace = usedSpace + usedSpaceTextView?.text = usedSpace + } + + fun setFreeSpace(freeSpace: String) { + this.freeSpace = freeSpace + freeSpaceTextView?.text = freeSpace + } + + fun setPathAndTitleForStorage(storageTitleAndPath: String) { + pathAndTitle = storageTitleAndPath + pathAndTitleTextView?.text = storageTitleAndPath + } } diff --git a/core/src/main/res/drawable/progress_bar_state.xml b/core/src/main/res/drawable/progress_bar_state.xml new file mode 100644 index 0000000000..46820b2162 --- /dev/null +++ b/core/src/main/res/drawable/progress_bar_state.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/core/src/main/res/layout/item_radio_button.xml b/core/src/main/res/layout/item_radio_button.xml deleted file mode 100644 index 98ac72d153..0000000000 --- a/core/src/main/res/layout/item_radio_button.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - diff --git a/core/src/main/res/layout/item_storage_preference.xml b/core/src/main/res/layout/item_storage_preference.xml new file mode 100644 index 0000000000..37435e1424 --- /dev/null +++ b/core/src/main/res/layout/item_storage_preference.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 33f542be5b..f9abd9b150 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -138,6 +138,8 @@ ZIM files download in-app are located in the external storage directory in a folder entitled Kiwix. Storage Current Folder + %s used + %s free Sorry we were unable to delete some files. You should try using a file manager instead. pause resume diff --git a/core/src/main/res/xml/preferences.xml b/core/src/main/res/xml/preferences.xml index d57b7c248d..b89015b41b 100644 --- a/core/src/main/res/xml/preferences.xml +++ b/core/src/main/res/xml/preferences.xml @@ -67,12 +67,12 @@ From 9e2ce9370f3620f17e577df897610997de675a5a Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Wed, 7 Aug 2024 19:08:42 +0530 Subject: [PATCH 6/7] Refactored the Setting Test case according to this new UI. --- .../kiwixmobile/settings/SettingsRobot.kt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt index 7278838986..90ca84df2c 100644 --- a/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt +++ b/app/src/androidTest/java/org/kiwix/kiwixmobile/settings/SettingsRobot.kt @@ -28,6 +28,7 @@ import androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem import androidx.test.espresso.matcher.ViewMatchers.hasDescendant import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withResourceName +import androidx.test.espresso.matcher.ViewMatchers.withSubstring import androidx.test.espresso.matcher.ViewMatchers.withText import applyWithViewHierarchyPrinting import org.hamcrest.Matchers @@ -36,6 +37,7 @@ import org.kiwix.kiwixmobile.BaseRobot import org.kiwix.kiwixmobile.Findable.StringId.TextId import org.kiwix.kiwixmobile.Findable.Text import org.kiwix.kiwixmobile.core.R +import org.kiwix.kiwixmobile.testutils.TestUtils.getResourceString import org.kiwix.kiwixmobile.testutils.TestUtils.testFlakyView /** @@ -61,6 +63,16 @@ class SettingsRobot : BaseRobot() { ) } + private fun clickRecyclerViewItemsContainingText(@StringRes vararg stringIds: Int) { + onView( + withResourceName("recycler_view") + ).perform( + actionOnItem( + hasDescendant(Matchers.anyOf(*stringIds.subStringMatchers())), ViewActions.click() + ) + ) + } + fun toggleBackToTopPref() { clickRecyclerViewItems(R.string.pref_back_to_top) } @@ -101,11 +113,11 @@ class SettingsRobot : BaseRobot() { } fun clickInternalStoragePreference() { - clickRecyclerViewItems(R.string.internal_storage) + clickRecyclerViewItemsContainingText(R.string.internal_storage) } fun clickExternalStoragePreference() { - clickRecyclerViewItems(R.string.external_storage) + clickRecyclerViewItemsContainingText(R.string.external_storage) } fun clickClearHistoryPreference() { @@ -181,4 +193,7 @@ class SettingsRobot : BaseRobot() { context.resources.getStringArray(R.array.pref_dark_modes_entries) private fun IntArray.matchers() = map(::withText).toTypedArray() + private fun IntArray.subStringMatchers() = map { + withSubstring(getResourceString(it)) + }.toTypedArray() } From 65297a79d2735386ffb4550186dd2e9b98c7237a Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Thu, 8 Aug 2024 11:08:02 +0530 Subject: [PATCH 7/7] Enhanced RTL Support for Storage Path TextView. * Ensured that the storage path and title are displayed correctly in both LTR and RTL layouts. --- core/src/main/res/layout/item_storage_preference.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/res/layout/item_storage_preference.xml b/core/src/main/res/layout/item_storage_preference.xml index 37435e1424..e45f9b2f2c 100644 --- a/core/src/main/res/layout/item_storage_preference.xml +++ b/core/src/main/res/layout/item_storage_preference.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="16dp"> + android:padding="@dimen/activity_horizontal_margin">