diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9833294..a1fc4c8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { implementation(libs.androidx.swiperefreshlayout) implementation(libs.androidx.recyclerview) implementation(libs.protolite.well.known.types) + implementation(libs.material) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/model/SortConfig.kt b/app/src/main/java/com/all/pdfreader/pro/app/model/SortConfig.kt new file mode 100644 index 0000000..04882d3 --- /dev/null +++ b/app/src/main/java/com/all/pdfreader/pro/app/model/SortConfig.kt @@ -0,0 +1,46 @@ +package com.all.pdfreader.pro.app.model + +import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity + +enum class SortField { + NAME, DATE, SIZE +} + +enum class SortDirection { + ASC, DESC +} + +data class SortConfig( + val field: SortField, + val direction: SortDirection +) { + fun toPreferenceString(): String = "${field.name.lowercase()}_${direction.name.lowercase()}" + + companion object { + fun fromPreferenceString(value: String): SortConfig { + val parts = value.split("_") + val field = SortField.valueOf(parts[0].uppercase()) + val direction = SortDirection.valueOf(parts[1].uppercase()) + return SortConfig(field, direction) + } + + fun default(): SortConfig = SortConfig(SortField.DATE, SortDirection.DESC) + } + + fun applySort(documents: List): List { + return when (field) { + SortField.NAME -> when (direction) { + SortDirection.ASC -> documents.sortedBy { it.fileName.lowercase() } + SortDirection.DESC -> documents.sortedByDescending { it.fileName.lowercase() } + } + SortField.DATE -> when (direction) { + SortDirection.ASC -> documents.sortedBy { it.lastModified } + SortDirection.DESC -> documents.sortedByDescending { it.lastModified } + } + SortField.SIZE -> when (direction) { + SortDirection.ASC -> documents.sortedBy { it.fileSize } + SortDirection.DESC -> documents.sortedByDescending { it.fileSize } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/sp/AppStore.kt b/app/src/main/java/com/all/pdfreader/pro/app/sp/AppStore.kt index d3bdfb0..f1fd920 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/sp/AppStore.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/sp/AppStore.kt @@ -1,6 +1,7 @@ package com.all.pdfreader.pro.app.sp import android.content.Context +import com.all.pdfreader.pro.app.model.SortConfig import com.all.pdfreader.pro.app.sp.store.Store import com.all.pdfreader.pro.app.sp.store.asStoreProvider @@ -14,9 +15,9 @@ class AppStore(context: Context) { key = PERMISSIONS_DIALOG_PROMPT, defaultValue = false ) - // 文档排序方式:name_asc, name_desc, date_asc, date_desc, size_asc, size_desc + // 文档排序配置 var documentSortType: String by store.string( - key = DOCUMENT_SORT_TYPE, defaultValue = "date_desc" + key = DOCUMENT_SORT_TYPE, defaultValue = SortConfig.default().toPreferenceString() ) companion object { diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt index a59be7f..a25c230 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/act/MainActivity.kt @@ -14,6 +14,10 @@ import com.all.pdfreader.pro.app.ui.fragment.FavoriteFrag import com.all.pdfreader.pro.app.ui.fragment.HomeFrag import com.all.pdfreader.pro.app.ui.fragment.RecentlyFrag import com.all.pdfreader.pro.app.ui.fragment.ToolsFrag +import com.all.pdfreader.pro.app.model.SortConfig +import com.all.pdfreader.pro.app.model.SortField +import com.all.pdfreader.pro.app.model.SortDirection +import com.all.pdfreader.pro.app.ui.dialog.SortDialogFragment import com.all.pdfreader.pro.app.util.AppUtils.setClickWithAnimation import com.all.pdfreader.pro.app.util.FileChangeObserver import com.all.pdfreader.pro.app.util.FileUtils @@ -48,7 +52,7 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) ImmersionBar.with(this).statusBarView(binding.view).statusBarDarkFont(true) - .navigationBarColor(R.color.white).init() + .navigationBarColor(R.color.black).init() setupFragments() setupNavigation() @@ -108,7 +112,10 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback } binding.sortingBtn.setClickWithAnimation { - showSortDialog() + SortDialogFragment(onOkClick = { it -> + // 通知当前fragment更新排序 + (activeFragment as? SortableFragment)?.onSortTypeChanged(it) + }).show(supportFragmentManager, TAG) } } @@ -119,36 +126,6 @@ class MainActivity : BaseActivity(), PermissionDialogFragment.PermissionCallback updateSelectedNav(target) } - private fun showSortDialog() { - val sortOptions = arrayOf( - "名称升序", "名称降序", - "日期升序", "日期降序", - "大小升序", "大小降序" - ) - val sortValues = arrayOf( - "name_asc", "name_desc", - "date_asc", "date_desc", - "size_asc", "size_desc" - ) - - val currentSort = appStore.documentSortType - val checkedItem = sortValues.indexOf(currentSort) - - android.app.AlertDialog.Builder(this) - .setTitle("选择排序方式") - .setSingleChoiceItems(sortOptions, checkedItem) { dialog, which -> - val newSortType = sortValues[which] - appStore.documentSortType = newSortType - - // 通知当前fragment更新排序 - (activeFragment as? SortableFragment)?.onSortTypeChanged(newSortType) - - dialog.dismiss() - } - .setNegativeButton("取消", null) - .show() - } - interface SortableFragment { fun onSortTypeChanged(sortType: String) } diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PermissionDialogFragment.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PermissionDialogFragment.kt index 8dfcde3..c11c654 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PermissionDialogFragment.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/PermissionDialogFragment.kt @@ -86,15 +86,15 @@ class PermissionDialogFragment : DialogFragment() { private fun showPermissionDeniedDialog() { AlertDialog.Builder(requireContext()) - .setTitle("权限被拒绝") - .setMessage("无法访问PDF文件。请在设置中授予存储权限") - .setPositiveButton("前往设置") { _, _ -> + .setTitle(getString(R.string.permission_denied)) + .setMessage(getString(R.string.pd_content_notice)) + .setPositiveButton(getString(R.string.go)) { _, _ -> val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) intent.data = "package:${requireContext().packageName}".toUri() startActivity(intent) dismiss() } - .setNegativeButton("退出") { _, _ -> + .setNegativeButton(getString(R.string.exit)) { _, _ -> dismiss() } .setCancelable(false) diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/SortDialogFragment.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/SortDialogFragment.kt new file mode 100644 index 0000000..b507d9d --- /dev/null +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/dialog/SortDialogFragment.kt @@ -0,0 +1,182 @@ +package com.all.pdfreader.pro.app.ui.dialog + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.all.pdfreader.pro.app.databinding.DialogPermissionBinding +import com.all.pdfreader.pro.app.databinding.DialogSortBinding +import com.all.pdfreader.pro.app.model.SortConfig +import com.all.pdfreader.pro.app.model.SortDirection +import com.all.pdfreader.pro.app.model.SortField +import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity +import com.all.pdfreader.pro.app.sp.AppStore +import com.all.pdfreader.pro.app.ui.act.MainActivity.SortableFragment +import com.google.android.material.bottomsheet.BottomSheetDialogFragment + +class SortDialogFragment( + private val onOkClick: (String) -> Unit +) : BottomSheetDialogFragment() { + + private lateinit var binding: DialogSortBinding + private lateinit var sortConfig: SortConfig + private lateinit var selectedField: SortField + private lateinit var selectedDirection: SortDirection + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View? { + binding = DialogSortBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val appStore = AppStore(requireActivity()) + isCancelable = false + sortConfig = SortConfig.fromPreferenceString(appStore.documentSortType) + selectedField = sortConfig.field + selectedDirection = sortConfig.direction + + setAllAlphaUnselected() + when (sortConfig.field) { + SortField.DATE -> { + setViewsAlpha(1f, binding.createdDateIv, binding.createdDateTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.createdDateGou) + } + + SortField.NAME -> { + setViewsAlpha(1f, binding.fileNameIv, binding.fileNameTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.fileNameGou) + } + + SortField.SIZE -> { + setViewsAlpha(1f, binding.fileSizeIv, binding.fileSizeTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.fileSizeGou) + } + } + when (sortConfig.direction) { + SortDirection.ASC -> { + setViewsAlpha(1f, binding.ascendingIv, binding.ascendingTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.ascendingGou) + } + + SortDirection.DESC -> { + setViewsAlpha(1f, binding.descendingIv, binding.descendingTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.descendingGou) + } + } + + binding.createdDateBtn.setOnClickListener { + setAllAlphaTop() + setViewsAlpha(1f, binding.createdDateIv, binding.createdDateTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.createdDateGou) + selectedField = SortField.DATE + } + + binding.fileNameBtn.setOnClickListener { + setAllAlphaTop() + setViewsAlpha(1f, binding.fileNameIv, binding.fileNameTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.fileNameGou) + selectedField = SortField.NAME + } + + binding.fileSizeBtn.setOnClickListener { + setAllAlphaTop() + setViewsAlpha(1f, binding.fileSizeIv, binding.fileSizeTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.fileSizeGou) + selectedField = SortField.SIZE + } + + binding.ascendingBtn.setOnClickListener { + setAllAlphaBoo() + setViewsAlpha(1f, binding.ascendingIv, binding.ascendingTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.ascendingGou) + selectedDirection = SortDirection.ASC + } + + binding.descendingBtn.setOnClickListener { + setAllAlphaBoo() + setViewsAlpha(1f, binding.descendingIv, binding.descendingTv) + setViewsGoneIsVISIBLE(View.VISIBLE, binding.descendingGou) + selectedDirection = SortDirection.DESC + } + + binding.cancelBtn.setOnClickListener { + dismiss() + } + binding.okBtn.setOnClickListener { + val newConfig = SortConfig( + field = selectedField, direction = selectedDirection + ) + appStore.documentSortType = newConfig.toPreferenceString() + onOkClick(appStore.documentSortType) + dismiss() + } + } + + private fun setAllAlphaTop() { + setViewsAlpha( + 0.5f, + binding.createdDateIv, + binding.createdDateTv, + binding.fileNameIv, + binding.fileNameTv, + binding.fileSizeIv, + binding.fileSizeTv, + ) + setViewsGoneIsVISIBLE( + View.GONE, + binding.createdDateGou, + binding.fileNameGou, + binding.fileSizeGou, + ) + } + + private fun setAllAlphaBoo() { + setViewsAlpha( + 0.5f, + binding.ascendingIv, + binding.ascendingTv, + binding.descendingIv, + binding.descendingTv + ) + setViewsGoneIsVISIBLE( + View.GONE, + binding.ascendingGou, + binding.descendingGou + ) + } + + private fun setAllAlphaUnselected() { + setViewsAlpha( + 0.5f, + binding.createdDateIv, + binding.createdDateTv, + binding.fileNameIv, + binding.fileNameTv, + binding.fileSizeIv, + binding.fileSizeTv, + binding.ascendingIv, + binding.ascendingTv, + binding.descendingIv, + binding.descendingTv + ) + setViewsGoneIsVISIBLE( + View.GONE, + binding.createdDateGou, + binding.fileNameGou, + binding.fileSizeGou, + binding.ascendingGou, + binding.descendingGou + ) + } + + private fun setViewsAlpha(alpha: Float, vararg views: View) { + views.forEach { it.alpha = alpha } + } + + private fun setViewsGoneIsVISIBLE(int: Int, vararg views: View) { + views.forEach { it.visibility = int } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt index 294999e..da388b9 100644 --- a/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt +++ b/app/src/main/java/com/all/pdfreader/pro/app/ui/fragment/HomeFrag.kt @@ -10,6 +10,7 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.LinearLayoutManager import com.all.pdfreader.pro.app.databinding.FragmentHomeBinding +import com.all.pdfreader.pro.app.model.SortConfig import com.all.pdfreader.pro.app.room.entity.PdfDocumentEntity import com.all.pdfreader.pro.app.room.repository.PdfRepository import com.all.pdfreader.pro.app.ui.act.MainActivity @@ -72,15 +73,8 @@ class HomeFrag : BaseFrag(), MainActivity.SortableFragment { } private fun sortDocuments(documents: List): List { - return when (appStore.documentSortType) { - "name_asc" -> documents.sortedBy { it.fileName.lowercase() } - "name_desc" -> documents.sortedByDescending { it.fileName.lowercase() } - "date_asc" -> documents.sortedBy { it.lastModified } - "date_desc" -> documents.sortedByDescending { it.lastModified } - "size_asc" -> documents.sortedBy { it.fileSize } - "size_desc" -> documents.sortedByDescending { it.fileSize } - else -> documents - } + val sortConfig = SortConfig.fromPreferenceString(appStore.documentSortType) + return sortConfig.applySort(documents) } } \ No newline at end of file diff --git a/app/src/main/res/drawable/dr_cancel_btn_bg.xml b/app/src/main/res/drawable/dr_cancel_btn_bg.xml new file mode 100644 index 0000000..f3621c9 --- /dev/null +++ b/app/src/main/res/drawable/dr_cancel_btn_bg.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/gou.xml b/app/src/main/res/drawable/gou.xml new file mode 100644 index 0000000..e2662d3 --- /dev/null +++ b/app/src/main/res/drawable/gou.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/dialog_sort.xml b/app/src/main/res/layout/dialog_sort.xml new file mode 100644 index 0000000..93ab105 --- /dev/null +++ b/app/src/main/res/layout/dialog_sort.xml @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index daf19d6..2fdb298 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -10,6 +10,17 @@ Allow access to manage all files Allow Access Go + Exit Notice Permission is required to access files + Cancel + OK + Sort by + Created Date + File Name + File Size + Ascending + Descending + 权限被拒绝 + Unable to access PDF file, please grant storage permission in settings \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1ca9e9a..af41e68 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ room_version = "2.7.2" swiperefreshlayout = "1.1.0" recyclerview = "1.4.0" protoliteWellKnownTypes = "18.0.1" +material = "1.12.0" [libraries] androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } @@ -30,6 +31,7 @@ immersionbar-ktx = { group = "com.geyifeng.immersionbar", name = "immersionbar-k androidx-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" } androidx-recyclerview = { group = "androidx.recyclerview", name = "recyclerview", version.ref = "recyclerview" } protolite-well-known-types = { group = "com.google.firebase", name = "protolite-well-known-types", version.ref = "protoliteWellKnownTypes" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }