Merge pull request #2800 from kiwix/Issue#2787

Change the method to get data directory path in Android 10+
This commit is contained in:
Kelson 2022-04-29 20:04:00 +02:00 committed by GitHub
commit ad5e2a47f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 290 additions and 52 deletions

View File

@ -18,8 +18,11 @@
package org.kiwix.kiwixmobile.nav.destination.library package org.kiwix.kiwixmobile.nav.destination.library
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.LayoutInflater import android.view.LayoutInflater
@ -28,6 +31,7 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
@ -51,15 +55,21 @@ import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.core.extensions.closeKeyboard import org.kiwix.kiwixmobile.core.extensions.closeKeyboard
import org.kiwix.kiwixmobile.core.extensions.snack import org.kiwix.kiwixmobile.core.extensions.snack
import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.main.CoreMainActivity import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.utils.BookUtils import org.kiwix.kiwixmobile.core.utils.BookUtils
import org.kiwix.kiwixmobile.core.utils.EXTERNAL_SELECT_POSITION
import org.kiwix.kiwixmobile.core.utils.INTERNAL_SELECT_POSITION
import org.kiwix.kiwixmobile.core.utils.NetworkUtils import org.kiwix.kiwixmobile.core.utils.NetworkUtils
import org.kiwix.kiwixmobile.core.utils.REQUEST_SELECT_FOLDER_PERMISSION
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.SimpleRecyclerViewScrollListener import org.kiwix.kiwixmobile.core.utils.SimpleRecyclerViewScrollListener
import org.kiwix.kiwixmobile.core.utils.SimpleTextListener import org.kiwix.kiwixmobile.core.utils.SimpleTextListener
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.SelectFolder
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.YesNoDialog.WifiOnly import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.YesNoDialog.WifiOnly
import org.kiwix.kiwixmobile.core.utils.files.FileUtils.getPathFromUri
import org.kiwix.kiwixmobile.zim_manager.NetworkState import org.kiwix.kiwixmobile.zim_manager.NetworkState
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel
import org.kiwix.kiwixmobile.zim_manager.library_view.AvailableSpaceCalculator import org.kiwix.kiwixmobile.zim_manager.library_view.AvailableSpaceCalculator
@ -242,8 +252,53 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
downloader.download(book) downloader.download(book)
} }
@SuppressLint("InflateParams")
private fun storeDeviceInPreferences(storageDevice: StorageDevice) { private fun storeDeviceInPreferences(storageDevice: StorageDevice) {
sharedPreferenceUtil.putPrefStorage(storageDevice.name) if (storageDevice.isInternal) {
sharedPreferenceUtil.putPrefStorage(
sharedPreferenceUtil.getPublicDirectoryPath(storageDevice.name)
)
sharedPreferenceUtil.putStoragePosition(INTERNAL_SELECT_POSITION)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
val view = LayoutInflater.from(activity).inflate(R.layout.select_folder_dialog, null)
dialogShower.show(SelectFolder { view }, ::selectFolder)
} else {
sharedPreferenceUtil.putPrefStorage(storageDevice.name)
sharedPreferenceUtil.putStoragePosition(EXTERNAL_SELECT_POSITION)
}
}
}
private fun selectFolder() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
)
startActivityForResult(intent, REQUEST_SELECT_FOLDER_PERMISSION)
}
@SuppressLint("WrongConstant") override fun onActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?
) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_SELECT_FOLDER_PERMISSION && resultCode == Activity.RESULT_OK) {
data?.let {
getPathFromUri(requireActivity(), data)?.let(sharedPreferenceUtil::putPrefStorage)
sharedPreferenceUtil.putStoragePosition(EXTERNAL_SELECT_POSITION)
} ?: run {
activity.toast(
resources
.getString(R.string.system_unable_to_grant_permission_message),
Toast.LENGTH_SHORT
)
}
}
} }
private fun onBookItemClick(item: LibraryListItem.BookItem) { private fun onBookItemClick(item: LibraryListItem.BookItem) {

View File

@ -41,7 +41,9 @@ class KiwixPrefsFragment : CorePrefsFragment() {
override fun setStorage() { override fun setStorage() {
findPreference<Preference>(PREF_STORAGE)?.title = getString( findPreference<Preference>(PREF_STORAGE)?.title = getString(
if (sharedPreferenceUtil.prefStorage == internalStorage()) R.string.internal_storage if (sharedPreferenceUtil.prefStorage == internalStorage()?.let
(sharedPreferenceUtil::getPublicDirectoryPath)
) R.string.internal_storage
else R.string.external_storage else R.string.external_storage
) )
findPreference<Preference>(PREF_STORAGE)?.summary = storageCalculator.calculateAvailableSpace() findPreference<Preference>(PREF_STORAGE)?.summary = storageCalculator.calculateAvailableSpace()

View File

@ -7,54 +7,62 @@ import kotlin.String
* `$ ./gradlew buildSrcVersions` * `$ ./gradlew buildSrcVersions`
*/ */
object Libs { object Libs {
/**
* For Getting Path of Selected Folder
**/
const val select_folder_document_file =
"androidx.documentfile:documentfile:" + Versions.document_file_version
/** /**
* https://github.com/Kotlin/kotlinx.coroutines * https://github.com/Kotlin/kotlinx.coroutines
*/ */
const val kotlinx_coroutines_android: String = const val kotlinx_coroutines_android: String =
"org.jetbrains.kotlinx:kotlinx-coroutines-android:" + "org.jetbrains.kotlinx:kotlinx-coroutines-android:" +
Versions.org_jetbrains_kotlinx_kotlinx_coroutines Versions.org_jetbrains_kotlinx_kotlinx_coroutines
/** /**
* https://github.com/Kotlin/kotlinx.coroutines * https://github.com/Kotlin/kotlinx.coroutines
*/ */
const val kotlinx_coroutines_test: String = "org.jetbrains.kotlinx:kotlinx-coroutines-test:" + const val kotlinx_coroutines_test: String = "org.jetbrains.kotlinx:kotlinx-coroutines-test:" +
Versions.org_jetbrains_kotlinx_kotlinx_coroutines Versions.org_jetbrains_kotlinx_kotlinx_coroutines
/** /**
* https://developer.android.com/testing * https://developer.android.com/testing
*/ */
const val espresso_contrib: String = "androidx.test.espresso:espresso-contrib:" + const val espresso_contrib: String = "androidx.test.espresso:espresso-contrib:" +
Versions.androidx_test_espresso Versions.androidx_test_espresso
/** /**
* https://developer.android.com/testing * https://developer.android.com/testing
*/ */
const val espresso_core: String = "androidx.test.espresso:espresso-core:" + const val espresso_core: String = "androidx.test.espresso:espresso-core:" +
Versions.androidx_test_espresso Versions.androidx_test_espresso
/** /**
* https://developer.android.com/testing * https://developer.android.com/testing
*/ */
const val espresso_intents: String = "androidx.test.espresso:espresso-intents:" + const val espresso_intents: String = "androidx.test.espresso:espresso-intents:" +
Versions.androidx_test_espresso Versions.androidx_test_espresso
/** /**
* https://developer.android.com/testing * https://developer.android.com/testing
*/ */
const val espresso_web: String = "androidx.test.espresso:espresso-web:" + const val espresso_web: String = "androidx.test.espresso:espresso-web:" +
Versions.androidx_test_espresso Versions.androidx_test_espresso
/** /**
* https://github.com/square/retrofit * https://github.com/square/retrofit
*/ */
const val adapter_rxjava2: String = "com.squareup.retrofit2:adapter-rxjava2:" + const val adapter_rxjava2: String = "com.squareup.retrofit2:adapter-rxjava2:" +
Versions.com_squareup_retrofit2 Versions.com_squareup_retrofit2
/** /**
* https://github.com/square/retrofit * https://github.com/square/retrofit
*/ */
const val converter_simplexml: String = "com.squareup.retrofit2:converter-simplexml:" + const val converter_simplexml: String = "com.squareup.retrofit2:converter-simplexml:" +
Versions.com_squareup_retrofit2 Versions.com_squareup_retrofit2
/** /**
* https://github.com/square/retrofit * https://github.com/square/retrofit
@ -65,13 +73,13 @@ object Libs {
* https://square.github.io/okhttp/ * https://square.github.io/okhttp/
*/ */
const val logging_interceptor: String = "com.squareup.okhttp3:logging-interceptor:" + const val logging_interceptor: String = "com.squareup.okhttp3:logging-interceptor:" +
Versions.com_squareup_okhttp3 Versions.com_squareup_okhttp3
/** /**
* https://square.github.io/okhttp/ * https://square.github.io/okhttp/
*/ */
const val mockwebserver: String = "com.squareup.okhttp3:mockwebserver:" + const val mockwebserver: String = "com.squareup.okhttp3:mockwebserver:" +
Versions.com_squareup_okhttp3 Versions.com_squareup_okhttp3
/** /**
* https://square.github.io/okhttp/ * https://square.github.io/okhttp/
@ -82,55 +90,55 @@ object Libs {
* https://kotlinlang.org/ * https://kotlinlang.org/
*/ */
const val kotlin_android_extensions: String = "org.jetbrains.kotlin:kotlin-android-extensions:" + const val kotlin_android_extensions: String = "org.jetbrains.kotlin:kotlin-android-extensions:" +
Versions.org_jetbrains_kotlin Versions.org_jetbrains_kotlin
/** /**
* https://kotlinlang.org/ * https://kotlinlang.org/
*/ */
const val kotlin_android_extensions_runtime: String = const val kotlin_android_extensions_runtime: String =
"org.jetbrains.kotlin:kotlin-android-extensions-runtime:" + Versions.org_jetbrains_kotlin "org.jetbrains.kotlin:kotlin-android-extensions-runtime:" + Versions.org_jetbrains_kotlin
/** /**
* https://kotlinlang.org/ * https://kotlinlang.org/
*/ */
const val kotlin_annotation_processing_gradle: String = const val kotlin_annotation_processing_gradle: String =
"org.jetbrains.kotlin:kotlin-annotation-processing-gradle:" + Versions.org_jetbrains_kotlin "org.jetbrains.kotlin:kotlin-annotation-processing-gradle:" + Versions.org_jetbrains_kotlin
/** /**
* https://kotlinlang.org/ * https://kotlinlang.org/
*/ */
const val kotlin_gradle_plugin: String = "org.jetbrains.kotlin:kotlin-gradle-plugin:" + const val kotlin_gradle_plugin: String = "org.jetbrains.kotlin:kotlin-gradle-plugin:" +
Versions.org_jetbrains_kotlin Versions.org_jetbrains_kotlin
/** /**
* https://kotlinlang.org/ * https://kotlinlang.org/
*/ */
const val kotlin_stdlib_jdk7: String = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:" + const val kotlin_stdlib_jdk7: String = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:" +
Versions.org_jetbrains_kotlin Versions.org_jetbrains_kotlin
/** /**
* https://developer.android.com/topic/libraries/architecture/index.html * https://developer.android.com/topic/libraries/architecture/index.html
*/ */
const val navigation_fragment_ktx: String = "androidx.navigation:navigation-fragment-ktx:" + const val navigation_fragment_ktx: String = "androidx.navigation:navigation-fragment-ktx:" +
Versions.androidx_navigation Versions.androidx_navigation
/** /**
* https://developer.android.com/topic/libraries/architecture/index.html * https://developer.android.com/topic/libraries/architecture/index.html
*/ */
const val navigation_safe_args_gradle_plugin: String = const val navigation_safe_args_gradle_plugin: String =
"androidx.navigation:navigation-safe-args-gradle-plugin:" + Versions.androidx_navigation "androidx.navigation:navigation-safe-args-gradle-plugin:" + Versions.androidx_navigation
/** /**
* https://developer.android.com/topic/libraries/architecture/index.html * https://developer.android.com/topic/libraries/architecture/index.html
*/ */
const val navigation_testing: String = "androidx.navigation:navigation-testing:" + const val navigation_testing: String = "androidx.navigation:navigation-testing:" +
Versions.androidx_navigation Versions.androidx_navigation
/** /**
* https://developer.android.com/topic/libraries/architecture/index.html * https://developer.android.com/topic/libraries/architecture/index.html
*/ */
const val navigation_ui_ktx: String = "androidx.navigation:navigation-ui-ktx:" + const val navigation_ui_ktx: String = "androidx.navigation:navigation-ui-ktx:" +
Versions.androidx_navigation Versions.androidx_navigation
/** /**
* https://github.com/google/dagger * https://github.com/google/dagger
@ -141,19 +149,19 @@ object Libs {
* https://github.com/google/dagger * https://github.com/google/dagger
*/ */
const val dagger_android: String = "com.google.dagger:dagger-android:" + const val dagger_android: String = "com.google.dagger:dagger-android:" +
Versions.com_google_dagger Versions.com_google_dagger
/** /**
* https://github.com/google/dagger * https://github.com/google/dagger
*/ */
const val dagger_android_processor: String = "com.google.dagger:dagger-android-processor:" + const val dagger_android_processor: String = "com.google.dagger:dagger-android-processor:" +
Versions.com_google_dagger Versions.com_google_dagger
/** /**
* https://github.com/google/dagger * https://github.com/google/dagger
*/ */
const val dagger_compiler: String = "com.google.dagger:dagger-compiler:" + const val dagger_compiler: String = "com.google.dagger:dagger-compiler:" +
Versions.com_google_dagger Versions.com_google_dagger
/** /**
* https://github.com/yahoo/squidb * https://github.com/yahoo/squidb
@ -164,13 +172,13 @@ object Libs {
* https://github.com/yahoo/squidb * https://github.com/yahoo/squidb
*/ */
const val squidb_annotations: String = "com.yahoo.squidb:squidb-annotations:" + const val squidb_annotations: String = "com.yahoo.squidb:squidb-annotations:" +
Versions.com_yahoo_squidb Versions.com_yahoo_squidb
/** /**
* https://github.com/yahoo/squidb * https://github.com/yahoo/squidb
*/ */
const val squidb_processor: String = "com.yahoo.squidb:squidb-processor:" + const val squidb_processor: String = "com.yahoo.squidb:squidb-processor:" +
Versions.com_yahoo_squidb Versions.com_yahoo_squidb
/** /**
* https://github.com/JakeWharton/butterknife/ * https://github.com/JakeWharton/butterknife/
@ -181,13 +189,13 @@ object Libs {
* https://github.com/JakeWharton/butterknife/ * https://github.com/JakeWharton/butterknife/
*/ */
const val butterknife_compiler: String = "com.jakewharton:butterknife-compiler:" + const val butterknife_compiler: String = "com.jakewharton:butterknife-compiler:" +
Versions.com_jakewharton Versions.com_jakewharton
/** /**
* https://github.com/JakeWharton/butterknife/ * https://github.com/JakeWharton/butterknife/
*/ */
const val butterknife_gradle_plugin: String = "com.jakewharton:butterknife-gradle-plugin:" + const val butterknife_gradle_plugin: String = "com.jakewharton:butterknife-gradle-plugin:" +
Versions.com_jakewharton Versions.com_jakewharton
/** /**
* https://developer.android.com/testing * https://developer.android.com/testing
@ -218,7 +226,7 @@ object Libs {
* https://objectbox.io * https://objectbox.io
*/ */
const val objectbox_gradle_plugin: String = "io.objectbox:objectbox-gradle-plugin:" + const val objectbox_gradle_plugin: String = "io.objectbox:objectbox-gradle-plugin:" +
Versions.io_objectbox Versions.io_objectbox
/** /**
* https://objectbox.io * https://objectbox.io
@ -234,7 +242,7 @@ object Libs {
* https://objectbox.io * https://objectbox.io
*/ */
const val objectbox_processor: String = "io.objectbox:objectbox-processor:" + const val objectbox_processor: String = "io.objectbox:objectbox-processor:" +
Versions.io_objectbox Versions.io_objectbox
/** /**
* https://objectbox.io * https://objectbox.io
@ -270,42 +278,42 @@ object Libs {
* https://developer.android.com/topic/libraries/architecture/index.html * https://developer.android.com/topic/libraries/architecture/index.html
*/ */
const val android_arch_lifecycle_extensions: String = "android.arch.lifecycle:extensions:" + const val android_arch_lifecycle_extensions: String = "android.arch.lifecycle:extensions:" +
Versions.android_arch_lifecycle_extensions Versions.android_arch_lifecycle_extensions
/** /**
* https://developer.android.com/studio * https://developer.android.com/studio
*/ */
const val com_android_tools_build_gradle: String = "com.android.tools.build:gradle:" + const val com_android_tools_build_gradle: String = "com.android.tools.build:gradle:" +
Versions.com_android_tools_build_gradle Versions.com_android_tools_build_gradle
const val de_fayard_buildsrcversions_gradle_plugin: String = const val de_fayard_buildsrcversions_gradle_plugin: String =
"de.fayard.buildSrcVersions:de.fayard.buildSrcVersions.gradle.plugin:" + "de.fayard.buildSrcVersions:de.fayard.buildSrcVersions.gradle.plugin:" +
Versions.de_fayard_buildsrcversions_gradle_plugin Versions.de_fayard_buildsrcversions_gradle_plugin
const val com_github_triplet_play_gradle_plugin: String = const val com_github_triplet_play_gradle_plugin: String =
"com.github.triplet.play:com.github.triplet.play.gradle.plugin:" + "com.github.triplet.play:com.github.triplet.play.gradle.plugin:" +
Versions.com_github_triplet_play_gradle_plugin Versions.com_github_triplet_play_gradle_plugin
/** /**
* http://jcp.org/en/jsr/detail?id=250 * http://jcp.org/en/jsr/detail?id=250
*/ */
const val javax_annotation_api: String = "javax.annotation:javax.annotation-api:" + const val javax_annotation_api: String = "javax.annotation:javax.annotation-api:" +
Versions.javax_annotation_api Versions.javax_annotation_api
const val ink_page_indicator: String = "com.pacioianu.david:ink-page-indicator:" + const val ink_page_indicator: String = "com.pacioianu.david:ink-page-indicator:" +
Versions.ink_page_indicator Versions.ink_page_indicator
/** /**
* http://github.com/square/leakcanary/ * http://github.com/square/leakcanary/
*/ */
const val leakcanary_android: String = "com.squareup.leakcanary:leakcanary-android:" + const val leakcanary_android: String = "com.squareup.leakcanary:leakcanary-android:" +
Versions.leakcanary_android Versions.leakcanary_android
/** /**
* http://tools.android.com * http://tools.android.com
*/ */
const val constraintlayout: String = "androidx.constraintlayout:constraintlayout:" + const val constraintlayout: String = "androidx.constraintlayout:constraintlayout:" +
Versions.constraintlayout Versions.constraintlayout
/** /**
* http://developer.android.com/tools/extras/support-library.html * http://developer.android.com/tools/extras/support-library.html
@ -323,7 +331,7 @@ object Libs {
const val junit_jupiter: String = "org.junit.jupiter:junit-jupiter:" + Versions.junit_jupiter const val junit_jupiter: String = "org.junit.jupiter:junit-jupiter:" + Versions.junit_jupiter
const val xfetch2okhttp: String = "androidx.tonyodev.fetch2okhttp:xfetch2okhttp:" + const val xfetch2okhttp: String = "androidx.tonyodev.fetch2okhttp:xfetch2okhttp:" +
Versions.xfetch2okhttp Versions.xfetch2okhttp
/** /**
* https://assertj.github.io/doc/ * https://assertj.github.io/doc/

View File

@ -11,6 +11,9 @@ import org.gradle.plugin.use.PluginDependencySpec
* YOU are responsible for updating manually the dependency version. * YOU are responsible for updating manually the dependency version.
*/ */
object Versions { object Versions {
const val document_file_version: String = "1.0.1"
const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.4.1" const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.4.1"
const val androidx_test_espresso: String = "3.3.0" const val androidx_test_espresso: String = "3.3.0"
@ -121,4 +124,4 @@ object Versions {
*/ */
val PluginDependenciesSpec.buildSrcVersions: PluginDependencySpec val PluginDependenciesSpec.buildSrcVersions: PluginDependencySpec
inline get() = inline get() =
id("de.fayard.buildSrcVersions").version(Versions.de_fayard_buildsrcversions_gradle_plugin) id("de.fayard.buildSrcVersions").version(Versions.de_fayard_buildsrcversions_gradle_plugin)

View File

@ -47,6 +47,9 @@ dependencies {
implementation(Libs.squidb_annotations) implementation(Libs.squidb_annotations)
add("kapt", Libs.squidb_processor) add("kapt", Libs.squidb_processor)
// Document File
implementation(Libs.select_folder_document_file)
// Square // Square
implementation(Libs.converter_simplexml) { implementation(Libs.converter_simplexml) {
exclude(group = "xpp3", module = "xpp3") exclude(group = "xpp3", module = "xpp3")

View File

@ -33,7 +33,7 @@ object StorageDeviceUtils {
@JvmStatic @JvmStatic
fun getReadableStorage(context: Context): List<StorageDevice> { fun getReadableStorage(context: Context): List<StorageDevice> {
val storageDevices = ArrayList<StorageDevice>().apply { val storageDevices = ArrayList<StorageDevice>().apply {
add(environmentDevices()) add(environmentDevices(context))
addAll(externalMountPointDevices()) addAll(externalMountPointDevices())
addAll(externalFilesDirsDevices(context, false)) addAll(externalFilesDirsDevices(context, false))
} }
@ -63,9 +63,9 @@ object StorageDeviceUtils {
?.map { dir -> StorageDevice(dir, false) } ?.map { dir -> StorageDevice(dir, false) }
.orEmpty() .orEmpty()
private fun environmentDevices() = private fun environmentDevices(context: Context) =
StorageDevice( StorageDevice(
generalisePath(Environment.getExternalStorageDirectory().path, false), generalisePath(context.getExternalFilesDir("").toString(), true),
Environment.isExternalStorageEmulated() Environment.isExternalStorageEmulated()
) )

View File

@ -43,7 +43,9 @@ internal class StorageViewHolder(
else R.string.external_storage else R.string.external_storage
) )
file_name.isChecked = sharedPreferenceUtil.prefStorage == item.name if (adapterPosition == sharedPreferenceUtil.storagePosition) {
file_name.isChecked = true
}
file_size.text = storageCalculator.calculateAvailableSpace(item.file) + " / " + file_size.text = storageCalculator.calculateAvailableSpace(item.file) + " / " +
storageCalculator.calculateTotalSpace(item.file) + " " storageCalculator.calculateTotalSpace(item.file) + " "
clickOverlay.setOnClickListener { clickOverlay.setOnClickListener {

View File

@ -20,11 +20,15 @@ package org.kiwix.kiwixmobile.core.settings;
import android.Manifest; import android.Manifest;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View;
import android.webkit.WebView; import android.webkit.WebView;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.preference.EditTextPreference; import androidx.preference.EditTextPreference;
@ -50,7 +54,13 @@ import org.kiwix.kiwixmobile.core.utils.LanguageUtils;
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil; import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil;
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower; import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower;
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog; import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog;
import org.kiwix.kiwixmobile.core.utils.files.FileUtils;
import static android.app.Activity.RESULT_OK;
import static android.content.Intent.ACTION_OPEN_DOCUMENT_TREE;
import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.EXTERNAL_SELECT_POSITION;
import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.INTERNAL_SELECT_POSITION;
import static org.kiwix.kiwixmobile.core.utils.ConstantsKt.REQUEST_SELECT_FOLDER_PERMISSION;
import static org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.PREF_NIGHT_MODE; import static org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.PREF_NIGHT_MODE;
import static org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.PREF_STORAGE; import static org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.PREF_STORAGE;
@ -175,9 +185,6 @@ public abstract class CorePrefsFragment extends PreferenceFragmentCompat impleme
versionPref.setSummary(getVersionName() + " Build: " + getVersionCode()); versionPref.setSummary(getVersionName() + " Build: " + getVersionCode());
} }
private int getVersionCode() { private int getVersionCode() {
try { try {
return getActivity().getPackageManager() return getActivity().getPackageManager()
@ -278,13 +285,47 @@ public abstract class CorePrefsFragment extends PreferenceFragmentCompat impleme
findPreference(PREF_STORAGE).setSummary( findPreference(PREF_STORAGE).setSummary(
storageCalculator.calculateAvailableSpace(storageDevice.getFile()) storageCalculator.calculateAvailableSpace(storageDevice.getFile())
); );
sharedPreferenceUtil.putPrefStorage(storageDevice.getName());
if (storageDevice.isInternal()) { if (storageDevice.isInternal()) {
sharedPreferenceUtil.putPrefStorage(
sharedPreferenceUtil.getPublicDirectoryPath(storageDevice.getName()));
findPreference(PREF_STORAGE).setTitle(getString(R.string.internal_storage)); findPreference(PREF_STORAGE).setTitle(getString(R.string.internal_storage));
sharedPreferenceUtil.putStoragePosition(INTERNAL_SELECT_POSITION);
} else { } else {
findPreference(PREF_STORAGE).setTitle(getString(R.string.external_storage)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
@SuppressLint("InflateParams") View view = LayoutInflater.from(getActivity()).inflate(R.layout.select_folder_dialog, null);
alertDialogShower.show(new KiwixDialog.SelectFolder(() -> view), () -> {
selectFolder();
return Unit.INSTANCE;
});
} else {
sharedPreferenceUtil.putPrefStorage(storageDevice.getName());
findPreference(PREF_STORAGE).setTitle(getString(R.string.external_storage));
sharedPreferenceUtil.putStoragePosition(EXTERNAL_SELECT_POSITION);
}
} }
return Unit.INSTANCE; return Unit.INSTANCE;
} }
private void selectFolder() {
Intent intent = new Intent(ACTION_OPEN_DOCUMENT_TREE);
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
| Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
startActivityForResult(intent, REQUEST_SELECT_FOLDER_PERMISSION);
}
@SuppressLint("WrongConstant") @Override
public void onActivityResult(int requestCode, int resultCode,
@Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_SELECT_FOLDER_PERMISSION && resultCode == RESULT_OK) {
sharedPreferenceUtil.putPrefStorage(
FileUtils.getPathFromUri(requireActivity(), data));
findPreference(PREF_STORAGE).setTitle(getString(R.string.external_storage));
sharedPreferenceUtil.putStoragePosition(EXTERNAL_SELECT_POSITION);
}
}
} }

View File

@ -23,6 +23,7 @@ const val CONTACT_EMAIL_ADDRESS = "android@kiwix.org"
// Request stuff // Request stuff
const val REQUEST_STORAGE_PERMISSION = 1 const val REQUEST_STORAGE_PERMISSION = 1
const val REQUEST_WRITE_STORAGE_PERMISSION_ADD_NOTE = 3 const val REQUEST_WRITE_STORAGE_PERMISSION_ADD_NOTE = 3
const val REQUEST_SELECT_FOLDER_PERMISSION = 4
// Tags // Tags
const val TAG_FILE_SEARCHED = "searchedarticle" const val TAG_FILE_SEARCHED = "searchedarticle"
@ -37,3 +38,7 @@ const val TAG_FROM_TAB_SWITCHER = "fromtabswitcher"
const val EXTRA_IS_WIDGET_VOICE = "isWidgetVoice" const val EXTRA_IS_WIDGET_VOICE = "isWidgetVoice"
const val HOTSPOT_SERVICE_CHANNEL_ID = "hotspotService" const val HOTSPOT_SERVICE_CHANNEL_ID = "hotspotService"
const val OLD_PROVIDER_DOMAIN = "org.kiwix.zim.base" const val OLD_PROVIDER_DOMAIN = "org.kiwix.zim.base"
// For Storage select dialog
const val INTERNAL_SELECT_POSITION = 0
const val EXTERNAL_SELECT_POSITION = 1

View File

@ -27,6 +27,7 @@ import io.reactivex.Flowable
import io.reactivex.processors.PublishProcessor import io.reactivex.processors.PublishProcessor
import org.kiwix.kiwixmobile.core.NightModeConfig import org.kiwix.kiwixmobile.core.NightModeConfig
import org.kiwix.kiwixmobile.core.NightModeConfig.Mode.Companion.from import org.kiwix.kiwixmobile.core.NightModeConfig.Mode.Companion.from
import org.kiwix.kiwixmobile.core.R
import java.io.File import java.io.File
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
@ -76,12 +77,20 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
get() { get() {
val storage = sharedPreferences.getString(PREF_STORAGE, null) val storage = sharedPreferences.getString(PREF_STORAGE, null)
return when { return when {
storage == null -> defaultStorage().also(::putPrefStorage) storage == null -> getPublicDirectoryPath(defaultStorage()).also {
!File(storage).exists() -> defaultStorage() putPrefStorage(it)
putStoragePosition(0)
}
!File(storage).exists() -> getPublicDirectoryPath(defaultStorage()).also {
putStoragePosition(0)
}
else -> storage else -> storage
} }
} }
val storagePosition: Int
get() = sharedPreferences.getInt(STORAGE_POSITION, 0)
private fun defaultStorage(): String = private fun defaultStorage(): String =
getExternalFilesDirs(context, null)[0]?.path getExternalFilesDirs(context, null)[0]?.path
?: context.filesDir.path // a workaround for emulators ?: context.filesDir.path // a workaround for emulators
@ -108,6 +117,10 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
_prefStorages.onNext(storage) _prefStorages.onNext(storage)
} }
fun putStoragePosition(pos: Int) {
sharedPreferences.edit { putInt(STORAGE_POSITION, pos) }
}
fun putPrefFullScreen(fullScreen: Boolean) = fun putPrefFullScreen(fullScreen: Boolean) =
sharedPreferences.edit { putBoolean(PREF_FULLSCREEN, fullScreen) } sharedPreferences.edit { putBoolean(PREF_FULLSCREEN, fullScreen) }
@ -160,10 +173,14 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
_textZooms.offer(textZoom) _textZooms.offer(textZoom)
} }
fun getPublicDirectoryPath(path: String): String =
path.substringBefore(context.getString(R.string.android_directory_seperator))
companion object { companion object {
// Prefs // Prefs
const val PREF_LANG = "pref_language_chooser" const val PREF_LANG = "pref_language_chooser"
const val PREF_STORAGE = "pref_select_folder" const val PREF_STORAGE = "pref_select_folder"
const val STORAGE_POSITION = "storage_position"
const val PREF_WIFI_ONLY = "pref_wifi_only" const val PREF_WIFI_ONLY = "pref_wifi_only"
const val PREF_KIWIX_MOBILE = "kiwix-mobile" const val PREF_KIWIX_MOBILE = "kiwix-mobile"
const val PREF_SHOW_INTRO = "showIntro" const val PREF_SHOW_INTRO = "showIntro"

View File

@ -185,6 +185,14 @@ sealed class KiwixDialog(
getView = customGetView getView = customGetView
) )
data class SelectFolder(val customGetView: (() -> View)?) : KiwixDialog(
R.string.select_folder,
null,
android.R.string.ok,
null,
getView = customGetView
)
object NotesDiscardConfirmation : KiwixDialog( object NotesDiscardConfirmation : KiwixDialog(
null, null,
R.string.confirmation_alert_dialog_message, R.string.confirmation_alert_dialog_message,

View File

@ -17,19 +17,25 @@
*/ */
package org.kiwix.kiwixmobile.core.utils.files package org.kiwix.kiwixmobile.core.utils.files
import android.annotation.SuppressLint
import android.app.Activity
import android.content.ContentUris import android.content.ContentUris
import android.content.Context import android.content.Context
import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Environment import android.os.Environment
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.documentfile.provider.DocumentFile
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.downloader.ChunkUtils import org.kiwix.kiwixmobile.core.downloader.ChunkUtils
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.core.extensions.get import org.kiwix.kiwixmobile.core.extensions.get
import org.kiwix.kiwixmobile.core.extensions.toast
import java.io.BufferedReader import java.io.BufferedReader
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.util.ArrayList
object FileUtils { object FileUtils {
@ -228,4 +234,44 @@ object FileUtils {
} catch (e: IOException) { } catch (e: IOException) {
"".also { e.printStackTrace() } "".also { e.printStackTrace() }
} }
@SuppressLint("WrongConstant")
@JvmStatic fun getPathFromUri(activity: Activity, data: Intent): String? {
val uri: Uri? = data.data
val takeFlags: Int = data.flags and (Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
uri?.let {
activity.grantUriPermission(
activity.packageName, it,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
activity.contentResolver.takePersistableUriPermission(it, takeFlags)
val dFile = DocumentFile.fromTreeUri(activity, it)
if (dFile != null) {
dFile.uri.path?.let { file ->
val originalPath = file.substring(
file.lastIndexOf(":") + 1
)
val path = "${activity.getExternalFilesDirs("")[1]}"
return@getPathFromUri path.substringBefore(
activity.getString(R.string.android_directory_seperator)
)
.plus(File.separator).plus(originalPath)
}
}
activity.toast(
activity.resources
.getString(R.string.system_unable_to_grant_permission_message),
Toast.LENGTH_SHORT
)
} ?: run {
activity.toast(
activity.resources
.getString(R.string.system_unable_to_grant_permission_message),
Toast.LENGTH_SHORT
)
}
return null
}
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Kiwix Android
~ Copyright (c) 2022 Kiwix <android.kiwix.org>
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
~
-->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/img"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginTop="15dp"
android:contentDescription="@string/app_name"
android:src="@drawable/select_folder_preview_image1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="8dp"
android:contentDescription="@string/app_name"
android:src="@drawable/select_folder_preview_image2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/img" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -295,6 +295,9 @@
<string name="all_files_permission_needed_message">In order to access all the zim files across device we need to have All Files Permission</string> <string name="all_files_permission_needed_message">In order to access all the zim files across device we need to have All Files Permission</string>
<string name="allowed">Allowed</string> <string name="allowed">Allowed</string>
<string name="not_allowed">Not allowed</string> <string name="not_allowed">Not allowed</string>
<string name="android_directory_seperator" translatable="false">/Android</string>
<string name="select_folder" translatable="false">Please select a folder for external storage.</string>
<string name="system_unable_to_grant_permission_message" translatable="false">System unable to grant permission!</string>
<string-array name="pref_night_modes_entries"> <string-array name="pref_night_modes_entries">
<item>@string/on</item> <item>@string/on</item>
<item>@string/off</item> <item>@string/off</item>