diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt index 1cc1acafb..b2b0c07f1 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt @@ -18,8 +18,11 @@ package org.kiwix.kiwixmobile.nav.destination.library +import android.annotation.SuppressLint +import android.app.Activity import android.content.Intent import android.net.ConnectivityManager +import android.os.Build import android.os.Bundle import android.provider.Settings import android.view.LayoutInflater @@ -28,6 +31,7 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.SearchView 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.closeKeyboard 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.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.REQUEST_SELECT_FOLDER_PERMISSION import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil import org.kiwix.kiwixmobile.core.utils.SimpleRecyclerViewScrollListener import org.kiwix.kiwixmobile.core.utils.SimpleTextListener import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower 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.files.FileUtils.getPathFromUri import org.kiwix.kiwixmobile.zim_manager.NetworkState import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel import org.kiwix.kiwixmobile.zim_manager.library_view.AvailableSpaceCalculator @@ -242,8 +252,53 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions { downloader.download(book) } + @SuppressLint("InflateParams") 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) { 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 948466170..f3ff379f4 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/settings/KiwixPrefsFragment.kt @@ -41,7 +41,9 @@ class KiwixPrefsFragment : CorePrefsFragment() { override fun setStorage() { findPreference(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 ) findPreference(PREF_STORAGE)?.summary = storageCalculator.calculateAvailableSpace() diff --git a/buildSrc/src/main/kotlin/Libs.kt b/buildSrc/src/main/kotlin/Libs.kt index feae21060..85306f39d 100644 --- a/buildSrc/src/main/kotlin/Libs.kt +++ b/buildSrc/src/main/kotlin/Libs.kt @@ -7,54 +7,62 @@ import kotlin.String * `$ ./gradlew buildSrcVersions` */ 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 */ const val kotlinx_coroutines_android: String = - "org.jetbrains.kotlinx:kotlinx-coroutines-android:" + + "org.jetbrains.kotlinx:kotlinx-coroutines-android:" + Versions.org_jetbrains_kotlinx_kotlinx_coroutines /** * https://github.com/Kotlin/kotlinx.coroutines */ 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 */ const val espresso_contrib: String = "androidx.test.espresso:espresso-contrib:" + - Versions.androidx_test_espresso + Versions.androidx_test_espresso /** * https://developer.android.com/testing */ const val espresso_core: String = "androidx.test.espresso:espresso-core:" + - Versions.androidx_test_espresso + Versions.androidx_test_espresso /** * https://developer.android.com/testing */ const val espresso_intents: String = "androidx.test.espresso:espresso-intents:" + - Versions.androidx_test_espresso + Versions.androidx_test_espresso /** * https://developer.android.com/testing */ const val espresso_web: String = "androidx.test.espresso:espresso-web:" + - Versions.androidx_test_espresso + Versions.androidx_test_espresso /** * https://github.com/square/retrofit */ const val adapter_rxjava2: String = "com.squareup.retrofit2:adapter-rxjava2:" + - Versions.com_squareup_retrofit2 + Versions.com_squareup_retrofit2 /** * https://github.com/square/retrofit */ const val converter_simplexml: String = "com.squareup.retrofit2:converter-simplexml:" + - Versions.com_squareup_retrofit2 + Versions.com_squareup_retrofit2 /** * https://github.com/square/retrofit @@ -65,13 +73,13 @@ object Libs { * https://square.github.io/okhttp/ */ const val logging_interceptor: String = "com.squareup.okhttp3:logging-interceptor:" + - Versions.com_squareup_okhttp3 + Versions.com_squareup_okhttp3 /** * https://square.github.io/okhttp/ */ const val mockwebserver: String = "com.squareup.okhttp3:mockwebserver:" + - Versions.com_squareup_okhttp3 + Versions.com_squareup_okhttp3 /** * https://square.github.io/okhttp/ @@ -82,55 +90,55 @@ object Libs { * https://kotlinlang.org/ */ const val kotlin_android_extensions: String = "org.jetbrains.kotlin:kotlin-android-extensions:" + - Versions.org_jetbrains_kotlin + Versions.org_jetbrains_kotlin /** * https://kotlinlang.org/ */ 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/ */ 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/ */ const val kotlin_gradle_plugin: String = "org.jetbrains.kotlin:kotlin-gradle-plugin:" + - Versions.org_jetbrains_kotlin + Versions.org_jetbrains_kotlin /** * https://kotlinlang.org/ */ 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 */ 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 */ 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 */ const val navigation_testing: String = "androidx.navigation:navigation-testing:" + - Versions.androidx_navigation + Versions.androidx_navigation /** * https://developer.android.com/topic/libraries/architecture/index.html */ const val navigation_ui_ktx: String = "androidx.navigation:navigation-ui-ktx:" + - Versions.androidx_navigation + Versions.androidx_navigation /** * https://github.com/google/dagger @@ -141,19 +149,19 @@ object Libs { * https://github.com/google/dagger */ const val dagger_android: String = "com.google.dagger:dagger-android:" + - Versions.com_google_dagger + Versions.com_google_dagger /** * https://github.com/google/dagger */ 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 */ const val dagger_compiler: String = "com.google.dagger:dagger-compiler:" + - Versions.com_google_dagger + Versions.com_google_dagger /** * https://github.com/yahoo/squidb @@ -164,13 +172,13 @@ object Libs { * https://github.com/yahoo/squidb */ const val squidb_annotations: String = "com.yahoo.squidb:squidb-annotations:" + - Versions.com_yahoo_squidb + Versions.com_yahoo_squidb /** * https://github.com/yahoo/squidb */ const val squidb_processor: String = "com.yahoo.squidb:squidb-processor:" + - Versions.com_yahoo_squidb + Versions.com_yahoo_squidb /** * https://github.com/JakeWharton/butterknife/ @@ -181,13 +189,13 @@ object Libs { * https://github.com/JakeWharton/butterknife/ */ const val butterknife_compiler: String = "com.jakewharton:butterknife-compiler:" + - Versions.com_jakewharton + Versions.com_jakewharton /** * https://github.com/JakeWharton/butterknife/ */ const val butterknife_gradle_plugin: String = "com.jakewharton:butterknife-gradle-plugin:" + - Versions.com_jakewharton + Versions.com_jakewharton /** * https://developer.android.com/testing @@ -218,7 +226,7 @@ object Libs { * https://objectbox.io */ const val objectbox_gradle_plugin: String = "io.objectbox:objectbox-gradle-plugin:" + - Versions.io_objectbox + Versions.io_objectbox /** * https://objectbox.io @@ -234,7 +242,7 @@ object Libs { * https://objectbox.io */ const val objectbox_processor: String = "io.objectbox:objectbox-processor:" + - Versions.io_objectbox + Versions.io_objectbox /** * https://objectbox.io @@ -270,42 +278,42 @@ object Libs { * https://developer.android.com/topic/libraries/architecture/index.html */ 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 */ 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 = - "de.fayard.buildSrcVersions:de.fayard.buildSrcVersions.gradle.plugin:" + + "de.fayard.buildSrcVersions:de.fayard.buildSrcVersions.gradle.plugin:" + Versions.de_fayard_buildsrcversions_gradle_plugin 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 /** * http://jcp.org/en/jsr/detail?id=250 */ 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:" + - Versions.ink_page_indicator + Versions.ink_page_indicator /** * http://github.com/square/leakcanary/ */ const val leakcanary_android: String = "com.squareup.leakcanary:leakcanary-android:" + - Versions.leakcanary_android + Versions.leakcanary_android /** * http://tools.android.com */ const val constraintlayout: String = "androidx.constraintlayout:constraintlayout:" + - Versions.constraintlayout + Versions.constraintlayout /** * 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 xfetch2okhttp: String = "androidx.tonyodev.fetch2okhttp:xfetch2okhttp:" + - Versions.xfetch2okhttp + Versions.xfetch2okhttp /** * https://assertj.github.io/doc/ diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 142cee923..9b5c75bf9 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -11,6 +11,9 @@ import org.gradle.plugin.use.PluginDependencySpec * YOU are responsible for updating manually the dependency version. */ object Versions { + + const val document_file_version: String = "1.0.1" + const val org_jetbrains_kotlinx_kotlinx_coroutines: String = "1.4.1" const val androidx_test_espresso: String = "3.3.0" @@ -121,4 +124,4 @@ object Versions { */ val PluginDependenciesSpec.buildSrcVersions: PluginDependencySpec inline get() = - id("de.fayard.buildSrcVersions").version(Versions.de_fayard_buildsrcversions_gradle_plugin) + id("de.fayard.buildSrcVersions").version(Versions.de_fayard_buildsrcversions_gradle_plugin) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index ad35718aa..b01ad950e 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -47,6 +47,9 @@ dependencies { implementation(Libs.squidb_annotations) add("kapt", Libs.squidb_processor) + // Document File + implementation(Libs.select_folder_document_file) + // Square implementation(Libs.converter_simplexml) { exclude(group = "xpp3", module = "xpp3") diff --git a/core/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt b/core/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt index d3a9bf8c4..b182b8a6b 100644 --- a/core/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt +++ b/core/src/main/java/eu/mhutti1/utils/storage/StorageDeviceUtils.kt @@ -33,7 +33,7 @@ object StorageDeviceUtils { @JvmStatic fun getReadableStorage(context: Context): List { val storageDevices = ArrayList().apply { - add(environmentDevices()) + add(environmentDevices(context)) addAll(externalMountPointDevices()) addAll(externalFilesDirsDevices(context, false)) } @@ -63,9 +63,9 @@ object StorageDeviceUtils { ?.map { dir -> StorageDevice(dir, false) } .orEmpty() - private fun environmentDevices() = + private fun environmentDevices(context: Context) = StorageDevice( - generalisePath(Environment.getExternalStorageDirectory().path, false), + generalisePath(context.getExternalFilesDir("").toString(), true), Environment.isExternalStorageEmulated() ) diff --git a/core/src/main/java/eu/mhutti1/utils/storage/adapter/StorageViewHolder.kt b/core/src/main/java/eu/mhutti1/utils/storage/adapter/StorageViewHolder.kt index e91c4b751..b949ba57d 100644 --- a/core/src/main/java/eu/mhutti1/utils/storage/adapter/StorageViewHolder.kt +++ b/core/src/main/java/eu/mhutti1/utils/storage/adapter/StorageViewHolder.kt @@ -43,7 +43,9 @@ internal class StorageViewHolder( 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) + " / " + storageCalculator.calculateTotalSpace(item.file) + " " clickOverlay.setOnClickListener { diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.java b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.java index 5a38303cf..7b7cebb82 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.java +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/settings/CorePrefsFragment.java @@ -20,11 +20,15 @@ package org.kiwix.kiwixmobile.core.settings; import android.Manifest; import android.annotation.SuppressLint; +import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; +import android.view.View; import android.webkit.WebView; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.navigation.NavController; 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.dialog.DialogShower; 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_STORAGE; @@ -175,9 +185,6 @@ public abstract class CorePrefsFragment extends PreferenceFragmentCompat impleme versionPref.setSummary(getVersionName() + " Build: " + getVersionCode()); } - - - private int getVersionCode() { try { return getActivity().getPackageManager() @@ -278,13 +285,47 @@ public abstract class CorePrefsFragment extends PreferenceFragmentCompat impleme findPreference(PREF_STORAGE).setSummary( storageCalculator.calculateAvailableSpace(storageDevice.getFile()) ); - sharedPreferenceUtil.putPrefStorage(storageDevice.getName()); + if (storageDevice.isInternal()) { + sharedPreferenceUtil.putPrefStorage( + sharedPreferenceUtil.getPublicDirectoryPath(storageDevice.getName())); findPreference(PREF_STORAGE).setTitle(getString(R.string.internal_storage)); + sharedPreferenceUtil.putStoragePosition(INTERNAL_SELECT_POSITION); } 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; } + 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); + } + } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt index 308849cc3..3376dbe4e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt @@ -23,6 +23,7 @@ const val CONTACT_EMAIL_ADDRESS = "android@kiwix.org" // Request stuff const val REQUEST_STORAGE_PERMISSION = 1 const val REQUEST_WRITE_STORAGE_PERMISSION_ADD_NOTE = 3 +const val REQUEST_SELECT_FOLDER_PERMISSION = 4 // Tags 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 HOTSPOT_SERVICE_CHANNEL_ID = "hotspotService" 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 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 19c4d0ac7..b7d0b67db 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 @@ -27,6 +27,7 @@ import io.reactivex.Flowable import io.reactivex.processors.PublishProcessor import org.kiwix.kiwixmobile.core.NightModeConfig import org.kiwix.kiwixmobile.core.NightModeConfig.Mode.Companion.from +import org.kiwix.kiwixmobile.core.R import java.io.File import java.util.Locale import javax.inject.Inject @@ -76,12 +77,20 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { get() { val storage = sharedPreferences.getString(PREF_STORAGE, null) return when { - storage == null -> defaultStorage().also(::putPrefStorage) - !File(storage).exists() -> defaultStorage() + storage == null -> getPublicDirectoryPath(defaultStorage()).also { + putPrefStorage(it) + putStoragePosition(0) + } + !File(storage).exists() -> getPublicDirectoryPath(defaultStorage()).also { + putStoragePosition(0) + } else -> storage } } + val storagePosition: Int + get() = sharedPreferences.getInt(STORAGE_POSITION, 0) + private fun defaultStorage(): String = getExternalFilesDirs(context, null)[0]?.path ?: context.filesDir.path // a workaround for emulators @@ -108,6 +117,10 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { _prefStorages.onNext(storage) } + fun putStoragePosition(pos: Int) { + sharedPreferences.edit { putInt(STORAGE_POSITION, pos) } + } + fun putPrefFullScreen(fullScreen: Boolean) = sharedPreferences.edit { putBoolean(PREF_FULLSCREEN, fullScreen) } @@ -160,10 +173,14 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) { _textZooms.offer(textZoom) } + fun getPublicDirectoryPath(path: String): String = + path.substringBefore(context.getString(R.string.android_directory_seperator)) + companion object { // Prefs const val PREF_LANG = "pref_language_chooser" const val PREF_STORAGE = "pref_select_folder" + const val STORAGE_POSITION = "storage_position" const val PREF_WIFI_ONLY = "pref_wifi_only" const val PREF_KIWIX_MOBILE = "kiwix-mobile" const val PREF_SHOW_INTRO = "showIntro" diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/dialog/KiwixDialog.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/dialog/KiwixDialog.kt index 7bb1139ac..c774b3401 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/dialog/KiwixDialog.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/dialog/KiwixDialog.kt @@ -185,6 +185,14 @@ sealed class KiwixDialog( getView = customGetView ) + data class SelectFolder(val customGetView: (() -> View)?) : KiwixDialog( + R.string.select_folder, + null, + android.R.string.ok, + null, + getView = customGetView + ) + object NotesDiscardConfirmation : KiwixDialog( null, R.string.confirmation_alert_dialog_message, diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/files/FileUtils.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/files/FileUtils.kt index dadb75e95..81f6a117e 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/files/FileUtils.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/files/FileUtils.kt @@ -17,19 +17,25 @@ */ package org.kiwix.kiwixmobile.core.utils.files +import android.annotation.SuppressLint +import android.app.Activity import android.content.ContentUris import android.content.Context +import android.content.Intent import android.net.Uri import android.os.Environment import android.provider.DocumentsContract 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.entity.LibraryNetworkEntity.Book import org.kiwix.kiwixmobile.core.extensions.get +import org.kiwix.kiwixmobile.core.extensions.toast import java.io.BufferedReader import java.io.File import java.io.IOException -import java.util.ArrayList object FileUtils { @@ -228,4 +234,44 @@ object FileUtils { } catch (e: IOException) { "".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 + } } diff --git a/core/src/main/res/drawable-hdpi/select_folder_preview_image1.png b/core/src/main/res/drawable-hdpi/select_folder_preview_image1.png new file mode 100644 index 000000000..d126231dd Binary files /dev/null and b/core/src/main/res/drawable-hdpi/select_folder_preview_image1.png differ diff --git a/core/src/main/res/drawable-hdpi/select_folder_preview_image2.png b/core/src/main/res/drawable-hdpi/select_folder_preview_image2.png new file mode 100644 index 000000000..e129e01dd Binary files /dev/null and b/core/src/main/res/drawable-hdpi/select_folder_preview_image2.png differ diff --git a/core/src/main/res/drawable-ldpi/select_folder_preview_image1.png b/core/src/main/res/drawable-ldpi/select_folder_preview_image1.png new file mode 100644 index 000000000..c8bb0fed8 Binary files /dev/null and b/core/src/main/res/drawable-ldpi/select_folder_preview_image1.png differ diff --git a/core/src/main/res/drawable-ldpi/select_folder_preview_image2.png b/core/src/main/res/drawable-ldpi/select_folder_preview_image2.png new file mode 100644 index 000000000..3a8a3b304 Binary files /dev/null and b/core/src/main/res/drawable-ldpi/select_folder_preview_image2.png differ diff --git a/core/src/main/res/drawable-mdpi/select_folder_preview_image1.png b/core/src/main/res/drawable-mdpi/select_folder_preview_image1.png new file mode 100644 index 000000000..78450c0ff Binary files /dev/null and b/core/src/main/res/drawable-mdpi/select_folder_preview_image1.png differ diff --git a/core/src/main/res/drawable-mdpi/select_folder_preview_image2.png b/core/src/main/res/drawable-mdpi/select_folder_preview_image2.png new file mode 100644 index 000000000..84d1afef4 Binary files /dev/null and b/core/src/main/res/drawable-mdpi/select_folder_preview_image2.png differ diff --git a/core/src/main/res/drawable-xhdpi/select_folder_preview_image1.png b/core/src/main/res/drawable-xhdpi/select_folder_preview_image1.png new file mode 100644 index 000000000..1ec9f3bf2 Binary files /dev/null and b/core/src/main/res/drawable-xhdpi/select_folder_preview_image1.png differ diff --git a/core/src/main/res/drawable-xhdpi/select_folder_preview_image2.png b/core/src/main/res/drawable-xhdpi/select_folder_preview_image2.png new file mode 100644 index 000000000..0ab78d226 Binary files /dev/null and b/core/src/main/res/drawable-xhdpi/select_folder_preview_image2.png differ diff --git a/core/src/main/res/drawable-xxhdpi/select_folder_preview_image1.png b/core/src/main/res/drawable-xxhdpi/select_folder_preview_image1.png new file mode 100644 index 000000000..75277b808 Binary files /dev/null and b/core/src/main/res/drawable-xxhdpi/select_folder_preview_image1.png differ diff --git a/core/src/main/res/drawable-xxhdpi/select_folder_preview_image2.png b/core/src/main/res/drawable-xxhdpi/select_folder_preview_image2.png new file mode 100644 index 000000000..5d664ef54 Binary files /dev/null and b/core/src/main/res/drawable-xxhdpi/select_folder_preview_image2.png differ diff --git a/core/src/main/res/drawable-xxxhdpi/select_folder_preview_image1.png b/core/src/main/res/drawable-xxxhdpi/select_folder_preview_image1.png new file mode 100644 index 000000000..509831452 Binary files /dev/null and b/core/src/main/res/drawable-xxxhdpi/select_folder_preview_image1.png differ diff --git a/core/src/main/res/drawable-xxxhdpi/select_folder_preview_image2.png b/core/src/main/res/drawable-xxxhdpi/select_folder_preview_image2.png new file mode 100644 index 000000000..d42bdc157 Binary files /dev/null and b/core/src/main/res/drawable-xxxhdpi/select_folder_preview_image2.png differ diff --git a/core/src/main/res/layout/select_folder_dialog.xml b/core/src/main/res/layout/select_folder_dialog.xml new file mode 100644 index 000000000..4c4465322 --- /dev/null +++ b/core/src/main/res/layout/select_folder_dialog.xml @@ -0,0 +1,45 @@ + + + + + + + + diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index e8b22ef18..9a3c61393 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -295,6 +295,9 @@ In order to access all the zim files across device we need to have All Files Permission Allowed Not allowed + /Android + Please select a folder for external storage. + System unable to grant permission! @string/on @string/off