Merge pull request #2803 from kiwix/Issue#2405

File picker feature is added.
This commit is contained in:
gouri-panda 2022-04-30 11:54:10 +05:30 committed by GitHub
commit 9a227f532d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 100 additions and 0 deletions

View File

@ -19,7 +19,10 @@
package org.kiwix.kiwixmobile.nav.destination.library package org.kiwix.kiwixmobile.nav.destination.library
import android.Manifest import android.Manifest
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
@ -29,10 +32,12 @@ 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.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -42,6 +47,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_destination_library.file_management_no_files import kotlinx.android.synthetic.main.fragment_destination_library.file_management_no_files
import kotlinx.android.synthetic.main.fragment_destination_library.go_to_downloads_button_no_files import kotlinx.android.synthetic.main.fragment_destination_library.go_to_downloads_button_no_files
import kotlinx.android.synthetic.main.fragment_destination_library.select_file
import kotlinx.android.synthetic.main.fragment_destination_library.zim_swiperefresh import kotlinx.android.synthetic.main.fragment_destination_library.zim_swiperefresh
import kotlinx.android.synthetic.main.fragment_destination_library.zimfilelist import kotlinx.android.synthetic.main.fragment_destination_library.zimfilelist
import org.kiwix.kiwixmobile.R import org.kiwix.kiwixmobile.R
@ -53,11 +59,13 @@ import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.core.extensions.toast 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.navigateToSettings import org.kiwix.kiwixmobile.core.navigateToSettings
import org.kiwix.kiwixmobile.core.utils.FILE_SELECT_CODE
import org.kiwix.kiwixmobile.core.utils.LanguageUtils import org.kiwix.kiwixmobile.core.utils.LanguageUtils
import org.kiwix.kiwixmobile.core.utils.REQUEST_STORAGE_PERMISSION import org.kiwix.kiwixmobile.core.utils.REQUEST_STORAGE_PERMISSION
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 org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BookOnDiskDelegate import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BookOnDiskDelegate
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskAdapter
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem
@ -67,6 +75,7 @@ import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.Re
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestNavigateTo import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestNavigateTo
import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect import org.kiwix.kiwixmobile.zim_manager.ZimManageViewModel.FileSelectActions.RequestSelect
import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState import org.kiwix.kiwixmobile.zim_manager.fileselect_view.FileSelectListState
import java.io.File
import javax.inject.Inject import javax.inject.Inject
private const val WAS_IN_ACTION_MODE = "WAS_IN_ACTION_MODE" private const val WAS_IN_ACTION_MODE = "WAS_IN_ACTION_MODE"
@ -139,6 +148,67 @@ class LocalLibraryFragment : BaseFragment() {
go_to_downloads_button_no_files.setOnClickListener { go_to_downloads_button_no_files.setOnClickListener {
offerAction(FileSelectActions.UserClickedDownloadBooksButton) offerAction(FileSelectActions.UserClickedDownloadBooksButton)
} }
select_file.setOnClickListener {
showFileChooser()
}
}
private fun showFileChooser() {
val intent = Intent().apply {
action = Intent.ACTION_GET_CONTENT
type = "*/*"
addCategory(Intent.CATEGORY_OPENABLE)
}
try {
startActivityForResult(
Intent.createChooser(intent, "Select a zim file"),
FILE_SELECT_CODE
)
} catch (ex: ActivityNotFoundException) {
activity.toast(resources.getString(R.string.no_app_found_to_open), Toast.LENGTH_SHORT)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
FILE_SELECT_CODE -> {
data?.data?.let { uri ->
getZimFileFromUri(uri)?.let(::navigateToReaderFragment)
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
private fun getZimFileFromUri(
uri: Uri
): File? {
val filePath = FileUtils.getLocalFilePathByUri(
requireActivity().applicationContext, uri
)
if (filePath == null || !File(filePath).exists()) {
activity.toast(R.string.error_file_not_found)
return null
}
val file = File(filePath)
return if (!FileUtils.isValidZimFile(file.path)) {
activity.toast(R.string.error_file_invalid)
null
} else {
file
}
}
private fun navigateToReaderFragment(file: File) {
if (!file.canRead()) {
activity.toast(R.string.unable_to_read_zim_file)
} else {
activity?.navigate(
LocalLibraryFragmentDirections.actionNavigationLibraryToNavigationReader()
.apply { zimFileUri = file.toUri().toString() }
)
}
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {

View File

@ -77,5 +77,17 @@
app:layout_constraintStart_toStartOf="@+id/file_management_no_files" app:layout_constraintStart_toStartOf="@+id/file_management_no_files"
app:layout_constraintTop_toBottomOf="@+id/file_management_no_files" /> app:layout_constraintTop_toBottomOf="@+id/file_management_no_files" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/select_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_marginBottom="60dp"
android:contentDescription="@string/app_name"
android:src="@drawable/ic_add_blue_24dp"
app:backgroundTint="@color/black"
app:fabSize="auto"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,4 +6,5 @@
<string name="cannot_open_file">Failed to open file\nPlease try looking for this file in the Device Tab of your Library</string> <string name="cannot_open_file">Failed to open file\nPlease try looking for this file in the Device Tab of your Library</string>
<string name="send_files_title">Send Files</string> <string name="send_files_title">Send Files</string>
<string name="receive_files_title">Receive Files</string> <string name="receive_files_title">Receive Files</string>
<string name="no_app_found_to_open">No app found to select zim file!</string>
</resources> </resources>

View File

@ -42,3 +42,5 @@ const val OLD_PROVIDER_DOMAIN = "org.kiwix.zim.base"
// For Storage select dialog // For Storage select dialog
const val INTERNAL_SELECT_POSITION = 0 const val INTERNAL_SELECT_POSITION = 0
const val EXTERNAL_SELECT_POSITION = 1 const val EXTERNAL_SELECT_POSITION = 1
const val FILE_SELECT_CODE = 5

View File

@ -36,6 +36,8 @@ 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.lang.Exception
import java.util.ArrayList
object FileUtils { object FileUtils {
@ -105,6 +107,11 @@ object FileUtils {
if (documentId[0] == "primary") { if (documentId[0] == "primary") {
return "${Environment.getExternalStorageDirectory()}/${documentId[1]}" return "${Environment.getExternalStorageDirectory()}/${documentId[1]}"
} }
return try {
"${getSdCardMainPath(context)}/${documentId[1]}"
} catch (ignore: Exception) {
null
}
} else if ("com.android.providers.downloads.documents" == uri.authority) } else if ("com.android.providers.downloads.documents" == uri.authority)
return try { return try {
documentProviderContentQuery(context, uri) documentProviderContentQuery(context, uri)
@ -235,6 +242,13 @@ object FileUtils {
"".also { e.printStackTrace() } "".also { e.printStackTrace() }
} }
@JvmStatic fun isValidZimFile(filePath: String): Boolean =
filePath.endsWith(".zim") || filePath.endsWith(".zimaa")
@JvmStatic fun getSdCardMainPath(context: Context): String =
"${context.getExternalFilesDirs("")[1]}"
.substringBefore(context.getString(R.string.android_directory_seperator))
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
@JvmStatic fun getPathFromUri(activity: Activity, data: Intent): String? { @JvmStatic fun getPathFromUri(activity: Activity, data: Intent): String? {
val uri: Uri? = data.data val uri: Uri? = data.data

View File

@ -45,6 +45,7 @@
<string name="server_started_message" tools:keep="@string/server_started_message">Enter this ip address into your browser to access the server %s</string> <string name="server_started_message" tools:keep="@string/server_started_message">Enter this ip address into your browser to access the server %s</string>
<string name="share_host_address">Share URL via other applications</string> <string name="share_host_address">Share URL via other applications</string>
<string name="error_file_not_found">Error: The selected ZIM file could not be found.</string> <string name="error_file_not_found">Error: The selected ZIM file could not be found.</string>
<string name="unable_to_read_zim_file">Unable to read this zim file!</string>
<string name="zim_not_opened">Unable to open zim file</string> <string name="zim_not_opened">Unable to open zim file</string>
<string name="error_file_invalid">Error: The selected file is not a valid ZIM file.</string> <string name="error_file_invalid">Error: The selected file is not a valid ZIM file.</string>
<string name="error_article_url_not_found">Error: Loading article (Url: %1$s) failed.</string> <string name="error_article_url_not_found">Error: Loading article (Url: %1$s) failed.</string>