diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 81f4f07..860fde3 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,7 +7,8 @@
-
+
+
@@ -38,6 +39,17 @@
android:supportsRtl="true"
android:theme="@style/Theme.PDFReaderPro"
tools:targetApi="36">
+
+
+
+
+
+ android:screenOrientation="portrait"
+ android:theme="@style/Theme.PDFReaderPro" />
=
requireParcelableArrayList(EXTRA_SELECTED_LIST)
@@ -197,6 +206,7 @@ class PdfResultActivity : BaseActivity() {
binding.progressBar.isIndeterminate = false
binding.progressBar.progress = 0
binding.progressBar.max = 100
+ binding.okBtnTv.text = getString(R.string.open)
}
val mergeInputFile: ArrayList =
requireParcelableArrayList(EXTRA_MERGE_LIST)
@@ -242,6 +252,7 @@ class PdfResultActivity : BaseActivity() {
val lockPassword = intent.getStringExtra(EXTRA_LOCK_UNLOCK_PASSWORD) ?: ""
runOnUiThread {
binding.congratulationsDesc.text = getString(R.string.set_password_successfully)
+ binding.okBtnTv.text = getString(R.string.open)
}
PdfSecurityUtils.setPasswordToPdfWithProgress(
filepath, lockPassword, lockPassword
@@ -265,6 +276,7 @@ class PdfResultActivity : BaseActivity() {
runOnUiThread {
binding.congratulationsDesc.text =
getString(R.string.remove_password_successfully)
+ binding.okBtnTv.text = getString(R.string.open)
}
PdfSecurityUtils.removePasswordFromPdfWithProgress(
filepath, unlockPassword
@@ -283,6 +295,9 @@ class PdfResultActivity : BaseActivity() {
finish()
return@launch
}
+ runOnUiThread {
+ binding.okBtnTv.text = getString(R.string.open)
+ }
val inputFiles: List = files.map { path -> File(path) }
val outputDir = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
@@ -316,6 +331,7 @@ class PdfResultActivity : BaseActivity() {
}
runOnUiThread {
binding.congratulationsDesc.text = getString(R.string.converted_successfully)
+ binding.okBtnTv.text = getString(R.string.save_to_album)
}
val selectedPages: ArrayList =
requireParcelableArrayList(EXTRA_PDF_TO_IMAGE_LIST)
@@ -353,6 +369,7 @@ class PdfResultActivity : BaseActivity() {
}
runOnUiThread {
binding.congratulationsDesc.text = getString(R.string.converted_successfully)
+ binding.okBtnTv.text = getString(R.string.save_to_album)
}
val selectedPages: ArrayList =
requireParcelableArrayList(EXTRA_PDF_TO_IMAGE_LIST)
@@ -374,14 +391,59 @@ class PdfResultActivity : BaseActivity() {
binding.progressTv.text = "$progressPercent"
}
}
- )?.let { file->
+ )?.let { file ->
val result = PdfSplitResultItem(file.absolutePath, file.absolutePath, false)
resultList.add(result)
}
}
withContext(Dispatchers.Main) {
binding.processingLayout.visibility = View.GONE
- // 默认选中第一个
+ if (resultList.isNotEmpty()) {
+ if (resultList.size == 1) {
+ binding.multipleImageLayout.visibility = View.GONE
+ binding.oneResultView.visibility = View.VISIBLE
+ binding.recyclerView.visibility = View.GONE
+ val item = resultList[0]
+ if (item.thumbnailPath != null) {
+ Glide.with(binding.root).load(File(item.thumbnailPath))
+ .transform(
+ CenterCrop(),
+ RoundedCorners(4.dpToPx(binding.root.context))
+ )
+ .into(binding.image)
+ }
+ if (item.isPassword) {
+ binding.lockLayout.visibility = View.VISIBLE
+ } else {
+ binding.lockLayout.visibility = View.GONE
+ }
+ binding.fileNameTv.text = File(item.filePath).name
+ binding.pathTv.text =
+ binding.root.context.getString(R.string.path_details, item.filePath)
+ } else {
+ if (source == PdfPickerSource.PDF_TO_IMAGES) {
+ binding.multipleImageLayout.visibility = View.VISIBLE
+ binding.oneResultView.visibility = View.GONE
+ binding.recyclerView.visibility = View.GONE
+ val item = resultList[0]
+ if (item.thumbnailPath != null) {
+ Glide.with(binding.root).load(File(item.thumbnailPath))
+ .transform(
+ CenterCrop(),
+ RoundedCorners(4.dpToPx(binding.root.context))
+ )
+ .into(binding.multipleImage)
+ }
+ binding.multipleNumberTv.text = "${resultList.size}"
+ binding.multipleImageTv.text = getString(R.string.number_image_converted,resultList.size)
+ } else {
+ binding.multipleImageLayout.visibility = View.GONE
+ binding.oneResultView.visibility = View.GONE
+ binding.recyclerView.visibility = View.VISIBLE
+ }
+ }
+ }
+ // 默认选中第一个,只有一个也是第一个,所以点击的地方就不做修改了。就展示这里做修改
if (resultList.isNotEmpty() && resultList.none { it.isSelected }) {
resultList[0].isSelected = true
}
@@ -398,20 +460,26 @@ class PdfResultActivity : BaseActivity() {
onBackPressedDispatcher.onBackPressed()
}
binding.shareBtn.setOnClickListener {
- val selectedItem = adapter.getSelectedItem()
- selectedItem?.let {
- AppUtils.shareFile(this, File(selectedItem.filePath))
+ if (source == PdfPickerSource.PDF_TO_IMAGES || source == PdfPickerSource.TO_LONG_IMAGE) {
+ AppUtils.shareMultipleFiles(this, adapter.getAllFiles())
+ } else {
+ val selectedItem = adapter.getSelectedItem()
+ selectedItem?.let {
+ AppUtils.shareFile(this, File(selectedItem.filePath))
+ }
}
}
binding.okBtn.setOnClickListener {
if (source == PdfPickerSource.PDF_TO_IMAGES || source == PdfPickerSource.TO_LONG_IMAGE) {
- val selectedItem = adapter.getSelectedItem()
- selectedItem?.let {
- val string = AppUtils.openImageFile(this, File(selectedItem.filePath))
- if (string.isNotEmpty()) {
- showToast(string)
- }
- openedExternal = true
+ if (isSaving) return@setOnClickListener
+
+ val imageFiles = adapter.getAllFiles()
+ val triggered = addImagesToGallery(this, imageFiles)
+
+ if (triggered) {
+ isSaving = true // 触发过就不允许重复点击
+ //2秒后恢复按钮
+ binding.okBtn.postDelayed({ isSaving = false }, 2000)
}
} else {
val selectedItem = adapter.getSelectedItem()
@@ -426,9 +494,6 @@ class PdfResultActivity : BaseActivity() {
override fun onResume() {
super.onResume()
- if (openedExternal) {
- finish()
- }
}
override fun onDestroy() {
@@ -490,4 +555,47 @@ class PdfResultActivity : BaseActivity() {
return result ?: default
}
+ /**
+ * 将指定图片文件列表同步到系统相册,并返回是否成功触发操作
+ *
+ * @param context 上下文
+ * @param imageFiles 要导入的图片文件列表
+ * @return true 表示触发扫描成功,false 表示无有效图片
+ */
+ fun addImagesToGallery(context: Context, imageFiles: List): Boolean {
+ if (imageFiles.isEmpty()) {
+ ToastUtils.show(context, context.getString(R.string.gallery_no_images_found))
+ return false
+ }
+
+ // 过滤有效图片
+ val validFiles = imageFiles.filter { file ->
+ file.exists() && file.isFile && (
+ file.extension.equals("jpg", true) ||
+ file.extension.equals("jpeg", true) ||
+ file.extension.equals("png", true) ||
+ file.extension.equals("webp", true)
+ )
+ }
+
+ if (validFiles.isEmpty()) {
+ ToastUtils.show(context, context.getString(R.string.gallery_no_images_found))
+ return false
+ }
+
+ val paths = validFiles.map { it.absolutePath }.toTypedArray()
+
+ // 扫描文件(异步触发系统媒体库)
+ MediaScannerConnection.scanFile(context, paths, null) { path, uri ->
+ Log.d("GallerySync", "Scanned: $path -> $uri")
+ }
+
+ ToastUtils.show(
+ context,
+ context.getString(R.string.gallery_saved_to_album, validFiles.size)
+ )
+
+ return true
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfResultAdapter.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfResultAdapter.kt
index 4f3ea01..1cdfe6b 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfResultAdapter.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/PdfResultAdapter.kt
@@ -74,4 +74,12 @@ class PdfResultAdapter(
fun getSelectedItem(): PdfSplitResultItem? {
return list.firstOrNull { it.isSelected }
}
+
+ fun getAllFiles(): List {
+ return list.mapNotNull {
+ val f = File(it.filePath)
+ if (f.exists()) f else null
+ }
+ }
+
}
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitSelectedPdfAdapter.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitSelectedPdfAdapter.kt
index f422619..dc8501c 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitSelectedPdfAdapter.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/adapter/SplitSelectedPdfAdapter.kt
@@ -34,7 +34,7 @@ class SplitSelectedPdfAdapter(
val thumbPath = item.getFirstThumbPath()
if (thumbPath.isNotEmpty()) {
Glide.with(holder.binding.root).load(File(thumbPath))
- .transform(CenterCrop(), RoundedCorners(8.dpToPx(holder.binding.root.context)))
+ .transform(CenterCrop(), RoundedCorners(4.dpToPx(holder.binding.root.context)))
.into(holder.binding.tvFileImg)
}
holder.binding.tvFileName.text = item.fileName
diff --git a/app/src/main/java/com/all/pdfreader/pro/app/util/AppUtils.kt b/app/src/main/java/com/all/pdfreader/pro/app/util/AppUtils.kt
index 21f944f..bab2b2b 100644
--- a/app/src/main/java/com/all/pdfreader/pro/app/util/AppUtils.kt
+++ b/app/src/main/java/com/all/pdfreader/pro/app/util/AppUtils.kt
@@ -117,7 +117,7 @@ object AppUtils {
fun shareFile(context: Context, file: File) {
try {
if (!file.exists()) {
- ToastUtils.show(context,context.getString(R.string.error_file_not_exist))
+ ToastUtils.show(context, context.getString(R.string.error_file_not_exist))
return
}
@@ -144,6 +144,60 @@ object AppUtils {
}
}
+ /**
+ * 分享多个文件(自动识别 MIME 类型)
+ * @param context 上下文
+ * @param files 要分享的文件列表
+ */
+ fun shareMultipleFiles(context: Context, files: List) {
+ try {
+ if (files.isEmpty()) {
+ ToastUtils.show(context, context.getString(R.string.error_file_not_exist))
+ return
+ }
+ if (files.size == 1) {
+ shareFile(context, files[0])
+ return
+ }
+
+ // 过滤掉不存在的文件
+ val validFiles = files.filter { it.exists() }
+ if (validFiles.isEmpty()) {
+ ToastUtils.show(context, context.getString(R.string.error_file_not_exist))
+ return
+ }
+
+ val uris = ArrayList()
+ for (file in validFiles) {
+ val uri = FileProvider.getUriForFile(
+ context,
+ "${context.packageName}.fileprovider",
+ file
+ )
+ uris.add(uri)
+ }
+
+ // 统一判断 MIME 类型(若不同类型,则用 "*/*")
+ val firstMime = getMimeType(validFiles.first())
+ val allSameMime = validFiles.all { getMimeType(it) == firstMime }
+ val mimeType = if (allSameMime) firstMime ?: "*/*" else "*/*"
+
+ val shareIntent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
+ type = mimeType
+ putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ }
+
+ context.startActivity(
+ Intent.createChooser(shareIntent, "share")
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ )
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
+
/**
* 根据文件扩展名自动获取 MIME 类型
*/
@@ -354,5 +408,4 @@ object AppUtils {
Toast.makeText(context, "Unable to share app info.", Toast.LENGTH_SHORT).show()
}
}
-
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/dr_collect_state_black_bg.xml b/app/src/main/res/drawable/dr_collect_state_black_bg.xml
new file mode 100644
index 0000000..5abad80
--- /dev/null
+++ b/app/src/main/res/drawable/dr_collect_state_black_bg.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_pdf_split.xml b/app/src/main/res/layout/activity_pdf_split.xml
index b28dfae..ec6cc56 100644
--- a/app/src/main/res/layout/activity_pdf_split.xml
+++ b/app/src/main/res/layout/activity_pdf_split.xml
@@ -133,6 +133,7 @@
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal"
+ android:orientation="vertical">
+
+
+
+
+
+
+
+
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
-
+ android:layout_margin="16dp"
+ android:gravity="center"
+ android:orientation="vertical">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:padding="24dp">
+
+
+
+
+
+
+
+
+
+
-
-
+
diff --git a/app/src/main/res/layout/adapter_selected_result_item.xml b/app/src/main/res/layout/adapter_selected_result_item.xml
index 7e2fb9b..e5c1b0a 100644
--- a/app/src/main/res/layout/adapter_selected_result_item.xml
+++ b/app/src/main/res/layout/adapter_selected_result_item.xml
@@ -15,8 +15,9 @@
android:orientation="horizontal">
+ android:layout_width="50dp"
+ android:layout_height="60dp" />
diff --git a/app/src/main/res/layout/adapter_split_selected_page_item.xml b/app/src/main/res/layout/adapter_split_selected_page_item.xml
index c4b6a0a..d948eaa 100644
--- a/app/src/main/res/layout/adapter_split_selected_page_item.xml
+++ b/app/src/main/res/layout/adapter_split_selected_page_item.xml
@@ -3,9 +3,13 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:background="@drawable/dr_rounded_corner_12_bg_white"
android:orientation="vertical"
- android:paddingStart="8dp"
- android:paddingTop="8dp"
+ android:paddingStart="16dp"
+ android:paddingTop="16dp"
+ android:paddingEnd="6dp"
+ android:paddingBottom="16dp"
tools:ignore="RtlSymmetry">
+ android:layout_width="66dp"
+ android:layout_height="80dp"
+ android:background="@drawable/dr_item_img_frame"
+ android:padding="1dp">
+ android:layout_width="66dp"
+ android:layout_height="80dp" />
@@ -87,6 +92,7 @@
@@ -128,13 +134,4 @@
android:src="@drawable/delete_cha_icon" />
-
-
-
-
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 55ce78d..829800a 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,5 +1,9 @@
+ #008577
+ #008577
+ #D81B60
+ #F0F5E5
#FFBB86FC
#FF6200EE
#FF3700B3
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 38a4bc5..cc1d5ac 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -185,4 +185,10 @@
File Viewing
How to enable "Reflow" mode?
"View mode" in the bottom bar. From there, you can toggle "Reflow" mode on or off.
Currently,this feature is only available on Android7 and above device.We will continue to optimize it and support more systems in the future.Please stay tuned.]]>
+ Target folder does not exist
+ No images found to import
+ Saved to gallery, total %1$d image(s)
+ Save to Album
+ 1 image converted
+ %1$d imagesF converted
\ No newline at end of file
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 1cf1feb..4155e99 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,8 +1,14 @@
-
-
+