From a204314d920e26c061acc470119e24b1e5363ca1 Mon Sep 17 00:00:00 2001 From: litingting Date: Mon, 29 Sep 2025 18:30:36 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AD=9B=E9=80=89=E5=92=8C=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 2 + app/src/main/AndroidManifest.xml | 1 + .../com/ux/video/file/filerecovery/App.kt | 14 + .../file/filerecovery/base/BaseAdapter.kt | 1 - .../file/filerecovery/base/NewBaseAdapter.kt | 50 ++ .../documents/DocumentsScanResultActivity.kt | 6 +- .../main/ScanSelectTypeActivity.kt | 30 +- .../photo/DatePickerDialogFragment.kt | 68 +++ .../filerecovery/photo/FilterPopupWindows.kt | 128 +++++ .../photo/PhotoDisplayDateAdapter.kt | 84 ++- .../photo/PhotoDisplayDateChildAdapter.kt | 250 ++++++--- .../photo/PhotoSortingActivity.kt | 390 +++++++++----- .../filerecovery/photo/ResultPhotosFiles.kt | 2 +- .../filerecovery/photo/SortDialogFragment.kt | 116 ++++ .../filerecovery/result/ExitDialogFragment.kt | 42 ++ .../filerecovery/result/ScanResultAdapter.kt | 9 +- .../result/ScanResultDisplayActivity.kt | 78 ++- .../filerecovery/result/ScanningActivity.kt | 30 +- .../utils/CircleImageProgressView.kt | 47 +- .../video/file/filerecovery/utils/Common.kt | 87 ++- .../file/filerecovery/utils/CustomTextView.kt | 21 +- .../filerecovery/utils/ExtendFunctions.kt | 8 +- .../utils/GridSpacingItemDecoration.kt | 45 +- .../file/filerecovery/utils/ScanManager.kt | 24 +- .../file/filerecovery/utils/ScanRepository.kt | 81 ++- .../main/res/color/selector_black_blue.xml | 6 + .../main/res/color/selector_c7c7cc_blue.xml | 6 + .../color/selector_delete_button_enable.xml | 7 + .../color/selector_recover_button_enable.xml | 7 + .../res/color/selector_switch_thumb_color.xml | 7 + .../res/color/selector_switch_track_color.xml | 7 + .../drawable/bg_dialog_btn_allow_solid_8.xml | 2 +- .../drawable/bg_dialog_btn_cancel_stoke_8.xml | 2 +- .../drawable/bg_rectangle_blue_right_8.xml | 7 + .../drawable/bg_rectangle_d5ebff_right_8.xml | 7 + .../drawable/bg_rectangle_f5f5fa_left_8.xml | 7 + ...white_20.xml => bg_rectangle_white_20.xml} | 0 .../drawable/bg_rectangle_white_bottom_20.xml | 7 + .../drawable/bg_rectangle_white_left_8.xml | 7 + .../drawable/bg_rectangle_white_top_20.xml | 7 + .../res/drawable/icon_arrow_down_gray.png | Bin 0 -> 436 bytes .../main/res/drawable/icon_arrow_up_blue.png | Bin 0 -> 552 bytes .../main/res/drawable/icon_ascending_date.png | Bin 0 -> 2480 bytes .../main/res/drawable/icon_ascending_size.png | Bin 0 -> 1187 bytes .../main/res/drawable/icon_checkmark_16dp.xml | 16 + .../main/res/drawable/icon_checkmark_28dp.xml | 16 + .../main/res/drawable/icon_checkmark_file.png | Bin 0 -> 458 bytes .../res/drawable/icon_checkmark_filter.png | Bin 0 -> 648 bytes .../res/drawable/icon_descending_date.png | Bin 0 -> 2428 bytes .../res/drawable/icon_descending_size.png | Bin 0 -> 1248 bytes app/src/main/res/drawable/icon_finish.png | Bin 0 -> 1611 bytes app/src/main/res/drawable/icon_selected.xml | 9 - app/src/main/res/drawable/icon_sort.png | Bin 0 -> 950 bytes ...nselected.xml => icon_unselected_16dp.xml} | 6 +- .../res/drawable/icon_unselected_28dp.xml | 9 + app/src/main/res/drawable/icon_vector.png | Bin 0 -> 379 bytes .../res/drawable/im_photo_center_image.png | Bin 0 -> 17649 bytes app/src/main/res/drawable/imag_no_photo.png | Bin 0 -> 26393 bytes app/src/main/res/drawable/photo_size_bg.xml | 2 +- .../res/drawable/selector_arrow_up_down.xml | 6 + .../drawable/selector_filter_image_view.xml | 5 + .../drawable/selector_icon_checkmark_28dp.xml | 5 + .../res/drawable/selector_icon_select.xml | 5 - .../res/drawable/selector_icon_select_16.xml | 5 + .../selector_recover_button_enable.xml | 6 + .../res/layout/activity_photo_sorting.xml | 500 ++++++++++++------ .../layout/activity_scan_result_display.xml | 190 +++++-- .../res/layout/common_layout_filter_item.xml | 27 + .../res/layout/common_layout_sort_item.xml | 45 ++ .../main/res/layout/dialog_date_picker.xml | 10 + app/src/main/res/layout/dialog_exit.xml | 74 +++ app/src/main/res/layout/dialog_permission.xml | 10 +- app/src/main/res/layout/dialog_sort.xml | 82 +++ .../layout/file_span_count_three_adapter.xml | 40 ++ .../layout/file_span_count_two_adapter.xml | 38 ++ .../res/layout/photo_display_date_adapter.xml | 49 +- .../photo_display_date_child_adapter.xml | 38 -- app/src/main/res/layout/popwindows_filter.xml | 27 + .../main/res/layout/scan_result_adapter.xml | 45 +- app/src/main/res/layout/test.xml | 32 +- app/src/main/res/values/attrs.xml | 9 +- app/src/main/res/values/colors.xml | 9 +- app/src/main/res/values/strings.xml | 40 ++ 83 files changed, 2409 insertions(+), 646 deletions(-) create mode 100644 app/src/main/java/com/ux/video/file/filerecovery/App.kt create mode 100644 app/src/main/java/com/ux/video/file/filerecovery/base/NewBaseAdapter.kt create mode 100644 app/src/main/java/com/ux/video/file/filerecovery/photo/DatePickerDialogFragment.kt create mode 100644 app/src/main/java/com/ux/video/file/filerecovery/photo/FilterPopupWindows.kt create mode 100644 app/src/main/java/com/ux/video/file/filerecovery/photo/SortDialogFragment.kt create mode 100644 app/src/main/java/com/ux/video/file/filerecovery/result/ExitDialogFragment.kt create mode 100644 app/src/main/res/color/selector_black_blue.xml create mode 100644 app/src/main/res/color/selector_c7c7cc_blue.xml create mode 100644 app/src/main/res/color/selector_delete_button_enable.xml create mode 100644 app/src/main/res/color/selector_recover_button_enable.xml create mode 100644 app/src/main/res/color/selector_switch_thumb_color.xml create mode 100644 app/src/main/res/color/selector_switch_track_color.xml create mode 100644 app/src/main/res/drawable/bg_rectangle_blue_right_8.xml create mode 100644 app/src/main/res/drawable/bg_rectangle_d5ebff_right_8.xml create mode 100644 app/src/main/res/drawable/bg_rectangle_f5f5fa_left_8.xml rename app/src/main/res/drawable/{bg_dialog_permission_white_20.xml => bg_rectangle_white_20.xml} (100%) create mode 100644 app/src/main/res/drawable/bg_rectangle_white_bottom_20.xml create mode 100644 app/src/main/res/drawable/bg_rectangle_white_left_8.xml create mode 100644 app/src/main/res/drawable/bg_rectangle_white_top_20.xml create mode 100644 app/src/main/res/drawable/icon_arrow_down_gray.png create mode 100644 app/src/main/res/drawable/icon_arrow_up_blue.png create mode 100644 app/src/main/res/drawable/icon_ascending_date.png create mode 100644 app/src/main/res/drawable/icon_ascending_size.png create mode 100644 app/src/main/res/drawable/icon_checkmark_16dp.xml create mode 100644 app/src/main/res/drawable/icon_checkmark_28dp.xml create mode 100644 app/src/main/res/drawable/icon_checkmark_file.png create mode 100644 app/src/main/res/drawable/icon_checkmark_filter.png create mode 100644 app/src/main/res/drawable/icon_descending_date.png create mode 100644 app/src/main/res/drawable/icon_descending_size.png create mode 100644 app/src/main/res/drawable/icon_finish.png delete mode 100644 app/src/main/res/drawable/icon_selected.xml create mode 100644 app/src/main/res/drawable/icon_sort.png rename app/src/main/res/drawable/{icon_unselected.xml => icon_unselected_16dp.xml} (81%) create mode 100644 app/src/main/res/drawable/icon_unselected_28dp.xml create mode 100644 app/src/main/res/drawable/icon_vector.png create mode 100644 app/src/main/res/drawable/im_photo_center_image.png create mode 100644 app/src/main/res/drawable/imag_no_photo.png create mode 100644 app/src/main/res/drawable/selector_arrow_up_down.xml create mode 100644 app/src/main/res/drawable/selector_filter_image_view.xml create mode 100644 app/src/main/res/drawable/selector_icon_checkmark_28dp.xml delete mode 100644 app/src/main/res/drawable/selector_icon_select.xml create mode 100644 app/src/main/res/drawable/selector_icon_select_16.xml create mode 100644 app/src/main/res/drawable/selector_recover_button_enable.xml create mode 100644 app/src/main/res/layout/common_layout_filter_item.xml create mode 100644 app/src/main/res/layout/common_layout_sort_item.xml create mode 100644 app/src/main/res/layout/dialog_date_picker.xml create mode 100644 app/src/main/res/layout/dialog_exit.xml create mode 100644 app/src/main/res/layout/dialog_sort.xml create mode 100644 app/src/main/res/layout/file_span_count_three_adapter.xml create mode 100644 app/src/main/res/layout/file_span_count_two_adapter.xml delete mode 100644 app/src/main/res/layout/photo_display_date_child_adapter.xml create mode 100644 app/src/main/res/layout/popwindows_filter.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f927f5b..25f7dc7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -52,4 +52,6 @@ dependencies { androidTestImplementation(libs.androidx.espresso.core) implementation (libs.glide) kapt (libs.compiler) + implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") + implementation ("com.google.android.material:material:1.13.0") } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fb9bf25..02dfa57 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,6 +13,7 @@ tools:ignore="ScopedStorage" /> ( protected val data: MutableList = mutableListOf() - fun addData(items: List?) { items?.let { val start = data.size diff --git a/app/src/main/java/com/ux/video/file/filerecovery/base/NewBaseAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/base/NewBaseAdapter.kt new file mode 100644 index 0000000..2021620 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/base/NewBaseAdapter.kt @@ -0,0 +1,50 @@ +package com.ux.video.file.filerecovery.base + +import android.content.Context +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView + + +abstract class NewBaseAdapter( + protected val mContext: Context +) : RecyclerView.Adapter() { + + protected val data: MutableList = mutableListOf() + + fun addData(items: List?) { + items?.let { + val start = data.size + data.addAll(it) + notifyItemRangeInserted(start, it.size) + } + } + + fun setData(items: List?) { + data.clear() + items?.let { data.addAll(it) } + notifyDataSetChanged() + } + + override fun getItemCount(): Int = data.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + return onCreateBinding(parent, viewType) + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + onBind(holder, data[position], getItemViewType(position)) + } + + /** + * 子类实现:根据 viewType 创建对应的 ViewHolder + */ + protected abstract fun onCreateBinding( + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder + + /** + * 子类实现:绑定数据 + */ + protected abstract fun onBind(holder: RecyclerView.ViewHolder, item: K, viewType: Int) +} diff --git a/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultActivity.kt index 3ea21dd..4949f1e 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/documents/DocumentsScanResultActivity.kt @@ -24,9 +24,9 @@ class DocumentsScanResultActivity : BaseActivity() { @@ -30,19 +39,20 @@ class ScanSelectTypeActivity : BaseActivity() { setSelectType(type) } + override fun initData() { super.initData() binding.imageBack.setOnClickListener { finish() } binding.scanAllFile.setOnClickListener { startActivity(Intent(this@ScanSelectTypeActivity, ScanningActivity::class.java).apply { - putExtra(ScanningActivity.Companion.KEY_SCAN_TYPE, allType) + putExtra(KEY_SCAN_TYPE, allType) }) } binding.scanDeletedFile.setOnClickListener { startActivity(Intent(this@ScanSelectTypeActivity, ScanningActivity::class.java).apply { - putExtra(ScanningActivity.Companion.KEY_SCAN_TYPE, deletedType) + putExtra(KEY_SCAN_TYPE, deletedType) }) } } @@ -50,26 +60,26 @@ class ScanSelectTypeActivity : BaseActivity() { private fun setSelectType(fileType: Int) { when (fileType) { VALUE_PHOTO -> { - allType = ScanningActivity.Companion.VALUE_SCAN_TYPE_photo - deletedType = ScanningActivity.Companion.VALUE_SCAN_TYPE_deleted_photo + allType = VALUE_SCAN_TYPE_photo + deletedType = VALUE_SCAN_TYPE_deleted_photo binding.title.text = getString(R.string.photo_title) } VALUE_VIDEO -> { - allType = ScanningActivity.Companion.VALUE_SCAN_TYPE_video - deletedType = ScanningActivity.Companion.VALUE_SCAN_TYPE_deleted_video + allType = VALUE_SCAN_TYPE_video + deletedType = VALUE_SCAN_TYPE_deleted_video binding.title.text = getString(R.string.video_title) } VALUE_AUDIO -> { - allType = ScanningActivity.Companion.VALUE_SCAN_TYPE_audio - deletedType = ScanningActivity.Companion.VALUE_SCAN_TYPE_deleted_audio + allType = VALUE_SCAN_TYPE_audio + deletedType = VALUE_SCAN_TYPE_deleted_audio binding.title.text = getString(R.string.audio_title) } VALUE_DOCUMENT -> { - allType = ScanningActivity.Companion.VALUE_SCAN_TYPE_documents - deletedType = ScanningActivity.Companion.VALUE_SCAN_TYPE_deleted_documents + allType = VALUE_SCAN_TYPE_documents + deletedType = VALUE_SCAN_TYPE_deleted_documents binding.title.text = getString(R.string.document_title) } } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/DatePickerDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/photo/DatePickerDialogFragment.kt new file mode 100644 index 0000000..b4fc24c --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/photo/DatePickerDialogFragment.kt @@ -0,0 +1,68 @@ +package com.ux.video.file.filerecovery.photo + +import android.graphics.Color +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.graphics.drawable.toDrawable +import androidx.fragment.app.DialogFragment +import com.ux.video.file.filerecovery.R +import com.ux.video.file.filerecovery.databinding.CommonLayoutSortItemBinding +import com.ux.video.file.filerecovery.databinding.DialogSortBinding +import com.google.android.material.datepicker.MaterialDatePicker +import com.google.android.material.datepicker.CalendarConstraints +import com.google.android.material.datepicker.DateValidatorPointForward +import java.text.SimpleDateFormat +import java.util.* + +class DatePickerDialogFragment(val onClickSort: (type: Int) -> Unit) : DialogFragment() { + + private lateinit var binding: DialogSortBinding + + override fun onStart() { + super.onStart() + dialog?.window?.apply { + setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + setGravity(Gravity.BOTTOM) + setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogSortBinding.inflate(inflater) + + return binding.root + } + + fun showDatePicker() { + // 创建日期选择器构建器 + val builder = MaterialDatePicker.Builder.datePicker() + builder.setTitleText("选择日期") + + // 可选:限制可选日期,比如只能选择今天之后的日期 + val constraintsBuilder = CalendarConstraints.Builder() + constraintsBuilder.setValidator(DateValidatorPointForward.now()) // 今天之后 + builder.setCalendarConstraints(constraintsBuilder.build()) + + val datePicker = builder.build() + + // 显示日期选择器 + datePicker.show(supportFragmentManager, "MATERIAL_DATE_PICKER") + + // 监听用户选择 + datePicker.addOnPositiveButtonClickListener { selection -> + // selection 是 Long 类型的时间戳(UTC 毫秒) + val sdf = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) + val dateString = sdf.format(Date(selection)) + println("用户选择的日期:$dateString") + } + } +} diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/FilterPopupWindows.kt b/app/src/main/java/com/ux/video/file/filerecovery/photo/FilterPopupWindows.kt new file mode 100644 index 0000000..2dac8a4 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/photo/FilterPopupWindows.kt @@ -0,0 +1,128 @@ +package com.ux.video.file.filerecovery.photo + +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.PopupWindow +import android.widget.RelativeLayout +import androidx.core.view.forEach +import com.ux.video.file.filerecovery.databinding.CommonLayoutFilterItemBinding +import com.ux.video.file.filerecovery.databinding.PopwindowsFilterBinding +import com.ux.video.file.filerecovery.utils.Common.setItemSelect +import com.ux.video.file.filerecovery.utils.CustomTextView + +class FilterPopupWindows( + context: Context, list: Array, + selectedPos: Int, + onClickConfirm: (value: String) -> Unit, + var onClickDismiss: () -> Unit +) : + PopupWindow(context) { + + private val viewBinding: PopwindowsFilterBinding = + PopwindowsFilterBinding.inflate(LayoutInflater.from(context)) + + init { + + setContentView(viewBinding.root) + + + width = ViewGroup.LayoutParams.MATCH_PARENT + height = ViewGroup.LayoutParams.WRAP_CONTENT + + isFocusable = true + isOutsideTouchable = true + setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + + // 动画(可选) +// animationStyle = R.style.PopupAnimation + + viewBinding.viewMask.setOnClickListener { + dismiss() + } + + setData(list, selectedPos, onClickConfirm) + } + + private fun setData( + list: Array, + selectedPos: Int, + onClickConfirm: (value: String) -> Unit + ) { + viewBinding.parentLayout.removeAllViews() + list.forEachIndexed { index, item -> + viewBinding.parentLayout.run { + addView( + CommonLayoutFilterItemBinding.inflate( + LayoutInflater.from(context), + this, + false + ).apply { + textItem.text = item + if (index == selectedPos) { + textItem.isSelected = true + checkImage.isSelected = true + } + }.root + ) + } + + } + setMutualExclusion(onClickConfirm) + + } + + + private fun setMutualExclusion(onClickConfirm: (value: String) -> Unit) { + viewBinding.run { + for (i in 0 until parentLayout.childCount) { + val child = parentLayout.getChildAt(i) + if (child is RelativeLayout) { + child.setOnClickListener { + for (j in 0 until parentLayout.childCount) { + val other = parentLayout.getChildAt(j) + if (other is RelativeLayout) { + setItemSelect(other, false) + + } + } + setItemSelect(child, true) + for (j in 0 until child.childCount) { + val other = child.getChildAt(j) + if (other is CustomTextView) { + onClickConfirm.invoke(other.text.toString()) + } + } + dismiss() + } + } + } + } + + } + + + fun show(anchor: View, xOff: Int = 0, yOff: Int = 0) { + showAsDropDown(anchor, xOff, yOff) + viewBinding.viewMask.apply { + alpha = 0f + animate().alpha(1f).setDuration(200).start() + visibility = View.VISIBLE + } + } + + override fun dismiss() { + Log.d("------------", "--------------dismiss") + viewBinding.viewMask.animate().alpha(0f).setDuration(200) + .withEndAction { + super.dismiss() + onClickDismiss.invoke() + }.start() + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateAdapter.kt index e0dfbb5..c5b542b 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateAdapter.kt @@ -1,61 +1,107 @@ package com.ux.video.file.filerecovery.photo +import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.GridLayoutManager import com.ux.video.file.filerecovery.base.BaseAdapter import com.ux.video.file.filerecovery.databinding.PhotoDisplayDateAdapterBinding -import com.ux.video.file.filerecovery.utils.ExtendFunctions.addItemDecorationOnce -import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx +import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.ExtendFunctions.resetItemDecorationOnce import com.ux.video.file.filerecovery.utils.GridSpacingItemDecoration +import com.ux.video.file.filerecovery.utils.ScanRepository -class PhotoDisplayDateAdapter(mContext: Context) : +class PhotoDisplayDateAdapter( + mContext: Context, + var mColumns: Int, + var viewModel: ScanRepository, + var onSelectedUpdate: (updatePath: String, isAdd: Boolean) -> Unit +) : BaseAdapter>, PhotoDisplayDateAdapterBinding>(mContext) { private var allSelected: Boolean? = null - private var columns = 3 override fun getViewBinding(parent: ViewGroup): PhotoDisplayDateAdapterBinding = PhotoDisplayDateAdapterBinding.inflate( LayoutInflater.from(parent.context), parent, false ) + + /** + * 返回所有嵌套的数据量总数 + */ + fun getTotalChildCount(): Int { + return data.sumOf { it.second.size } + } + + /** + * activity页面上点击全选按钮执行 + */ fun setAllSelected(allSelect: Boolean) { allSelected = allSelect + data.forEach { + it.second.forEach { item -> + onSelectedUpdate(item.path.toString(),allSelect) + } + } notifyDataSetChanged() } - fun setColumns(int: Int){ - columns = int + + fun resetAllValue(b: Boolean?){ + allSelected = b + } + + fun setColumns(int: Int) { + mColumns = int notifyDataSetChanged() } + + + + + @SuppressLint("SetTextI18n") override fun bindItem( holder: VHolder, item: Pair> ) { holder.vb.run { - val photoDisplayDateChildAdapter = PhotoDisplayDateChildAdapter(mContext) + item.run { - allSelected?.let { - imSelectStatus.isSelected = it - photoDisplayDateChildAdapter.setAllSelected(it) - } - imSelectStatus.setOnClickListener { - it.isSelected = !it.isSelected - photoDisplayDateChildAdapter.setAllSelected(it.isSelected) - } val (date, files) = item + val childAdapter = PhotoDisplayDateChildAdapter( + mContext, + mColumns, + viewModel + ) { path, addOrRemove, isDateAllSelected -> + //点击当前Adapter某一天的全选或者子Item上的选中都会回调到这里 + tvDayAllSelect.isSelected = isDateAllSelected + onSelectedUpdate(path.toString(),addOrRemove) + }.apply { setData(files) } + + allSelected?.let { + childAdapter.setAllSelected(it) + } + tvDayAllSelect.setOnClickListener { + it.isSelected = !it.isSelected + childAdapter.setAllSelected(it.isSelected) + } + textDate.text = date + textChildCounts.text = "(${files.size})" recyclerChild.apply { - layoutManager = GridLayoutManager(context, columns) + layoutManager = GridLayoutManager(context, mColumns) val gridSpacingItemDecoration = - GridSpacingItemDecoration(4, 8.dpToPx(mContext), true) - addItemDecorationOnce(gridSpacingItemDecoration) - adapter = photoDisplayDateChildAdapter.apply { setData(files) } + GridSpacingItemDecoration(mColumns, Common.itemSpacing, Common.horizontalSpacing) + Common.showLog("---------mColumns=${mColumns}") +// resetItemDecorationOnce(gridSpacingItemDecoration) + adapter = childAdapter isNestedScrollingEnabled = false } } } } + + } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateChildAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateChildAdapter.kt index 9dfe517..7ee9c0b 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateChildAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoDisplayDateChildAdapter.kt @@ -3,7 +3,11 @@ package com.ux.video.file.filerecovery.photo import android.content.Context import android.graphics.drawable.Drawable import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.ImageView +import android.widget.RelativeLayout +import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException @@ -12,89 +16,193 @@ import com.bumptech.glide.load.resource.bitmap.RoundedCorners import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.target.Target -import com.ux.video.file.filerecovery.base.BaseAdapter -import com.ux.video.file.filerecovery.databinding.PhotoDisplayDateChildAdapterBinding +import com.ux.video.file.filerecovery.App +import com.ux.video.file.filerecovery.base.NewBaseAdapter +import com.ux.video.file.filerecovery.databinding.FileSpanCountThreeAdapterBinding +import com.ux.video.file.filerecovery.databinding.FileSpanCountTwoAdapterBinding import com.ux.video.file.filerecovery.utils.Common +import com.ux.video.file.filerecovery.utils.CustomTextView import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import com.ux.video.file.filerecovery.utils.ScanManager import com.ux.video.file.filerecovery.utils.ScanRepository -import kotlin.collections.remove -class PhotoDisplayDateChildAdapter(mContext: Context) : - BaseAdapter(mContext) { - - private var allSelected: Boolean? = null - override fun getViewBinding(parent: ViewGroup): PhotoDisplayDateChildAdapterBinding = - PhotoDisplayDateChildAdapterBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) +class PhotoDisplayDateChildAdapter( + mContext: Context, + var mColumns: Int, + var viewModel: ScanRepository, + /** + * 通知外层某天全选的按钮更新状态 + * @param updatePath 当前点击的item + * @param addOrRemove 选中还是取消选中 + * @param dateAllSelected 这组数据是否全部选中(某一天) + */ + var onSelectedUpdate: (updatePath: String, addOrRemove: Boolean, dateAllSelected: Boolean) -> Unit +) : + NewBaseAdapter(mContext) { - fun setAllSelected(allselect: Boolean) { - allSelected = allselect + //日期组某一天的数据选择状态维护 + val dateSelectedMap = mutableSetOf() + + companion object { + private const val TYPE_TWO = 2 + private const val TYPE_THREE = 3 + private const val TYPE_FOUR = 4 + } + + + fun setAllSelected(isAdd: Boolean) { + data.forEach { + addOrRemove(it.path.toString(), isAdd) + } notifyDataSetChanged() } - override fun bindItem( - holder: VHolder, - item: ResultPhotosFiles - ) { - - holder.vb.run { - item.run { - imSelectStatus.isSelected = ScanRepository.instance.checkIsSelect(path.toString()) - allSelected?.let { - imSelectStatus.isSelected = it - //全选按钮手动触发,需要更新 - updateSetList(it, path.toString()) - } - imSelectStatus.setOnClickListener { - it.isSelected = !it.isSelected - updateSetList(it.isSelected, path.toString()) - - } - textSize.text = Common.formatFileSize(mContext, size) - Glide.with(mContext) - .load(targetFile) - .apply( - RequestOptions() - .transform(CenterCrop(), RoundedCorners(15.dpToPx(mContext))) - ) - .listener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target, - isFirstResource: Boolean - ): Boolean { - ScanManager.showLog( - "加载图片", - "-------path = ${path} file=${targetFile}" - ) - return false - } - - override fun onResourceReady( - resource: Drawable, - model: Any, - target: Target?, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - - return false - } - - }) - .into(imageThumbnail) - - } + override fun getItemViewType(position: Int): Int { + return when (mColumns) { + 2 -> TYPE_TWO + 3 -> TYPE_THREE + 4 -> TYPE_FOUR + else -> TYPE_THREE } } - private fun updateSetList(boolean: Boolean, path: String) { - ScanRepository.instance.toggleSelection(boolean,path) + fun setColumns(int: Int) { + mColumns = int + notifyDataSetChanged() } + + /** + * 设置View为正方形 + */ + private fun setHeight(view: View) { + val layoutParams = view.layoutParams + val screenWidth = view.context.resources.displayMetrics.widthPixels + val i = (Common.itemSpacing).dpToPx(App.mAppContext) * (mColumns - 1) + val itemSize = + (screenWidth - i - 2 * Common.horizontalSpacing.dpToPx(App.mAppContext) )/ mColumns + view.layoutParams = layoutParams.apply { + height = itemSize + } + } + + override fun onCreateBinding( + parent: ViewGroup, + viewType: Int + ): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return when (viewType) { + TYPE_TWO -> TwoHolder( + FileSpanCountTwoAdapterBinding.inflate( + inflater, + parent, + false + ) + ) + + else -> ThreeHolder( + FileSpanCountThreeAdapterBinding.inflate( + inflater, + parent, + false + ) + ) + } + + } + + override fun onBind( + holder: RecyclerView.ViewHolder, + item: ResultPhotosFiles, + viewType: Int + ) { + + when (holder) { + is TwoHolder -> holder.vb.run { + initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item) + } + + is ThreeHolder -> holder.vb.run { + initDateView(rootLayout, imageSelect, textSize, imageThumbnail, item) + } + } + + } + + class ThreeHolder(val vb: FileSpanCountThreeAdapterBinding) : + RecyclerView.ViewHolder(vb.root) + + class TwoHolder(val vb: FileSpanCountTwoAdapterBinding) : + RecyclerView.ViewHolder(vb.root) + + + private fun initDateView( + rootLayout: RelativeLayout, + imageSelectStatus: ImageView, + textSize: CustomTextView, + imageThumbnail: ImageView, + item: ResultPhotosFiles + ) { + item.run { + viewModel.checkIsSelect(path.toString()).let { + imageSelectStatus.isSelected = it + addOrRemove(path.toString(), it) + } + imageSelectStatus.setOnClickListener { + it.isSelected = !it.isSelected + it.isSelected.let { newStatus -> + addOrRemove(path.toString(), newStatus) + } + } + textSize.text = Common.formatFileSize(mContext, size) + + Glide.with(mContext) + .load(targetFile) + .apply( + RequestOptions() + .transform(CenterCrop(), RoundedCorners(8.dpToPx(mContext))) + ) + .listener(object : RequestListener { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target, + isFirstResource: Boolean + ): Boolean { + ScanManager.showLog( + "加载图片", + "-------path = ${path} file=${targetFile}" + ) + return false + } + + override fun onResourceReady( + resource: Drawable, + model: Any, + target: Target?, + dataSource: DataSource, + isFirstResource: Boolean + ): Boolean { + + return false + } + + }) + .into(imageThumbnail) + setHeight(rootLayout) + + } + + } + + private fun addOrRemove(path: String, boolean: Boolean) { + if (boolean) { + dateSelectedMap.add(path) + } else { + dateSelectedMap.remove(path) + } + onSelectedUpdate.invoke(path, boolean, dateSelectedMap.size == itemCount) + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoSortingActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoSortingActivity.kt index a5ea72b..f0a3d62 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoSortingActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/photo/PhotoSortingActivity.kt @@ -1,14 +1,19 @@ package com.ux.video.file.filerecovery.photo import android.view.LayoutInflater +import android.widget.LinearLayout +import androidx.activity.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.databinding.ActivityPhotoSortingBinding import com.ux.video.file.filerecovery.utils.Common -import com.ux.video.file.filerecovery.utils.ExtendFunctions.addItemDecorationOnce +import com.ux.video.file.filerecovery.utils.Common.setItemSelect import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterBySize import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterBySizeList @@ -16,6 +21,7 @@ import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinMonths import com.ux.video.file.filerecovery.utils.ExtendFunctions.filterWithinMonthsList import com.ux.video.file.filerecovery.utils.ExtendFunctions.getParcelableArrayListExtraCompat import com.ux.video.file.filerecovery.utils.ExtendFunctions.mbToBytes +import com.ux.video.file.filerecovery.utils.ExtendFunctions.resetItemDecorationOnce import com.ux.video.file.filerecovery.utils.GridSpacingItemDecoration import com.ux.video.file.filerecovery.utils.ScanManager import com.ux.video.file.filerecovery.utils.ScanManager.copySelectedFilesAsync @@ -38,12 +44,19 @@ class PhotoSortingActivity : BaseActivity() { val FILTER_SIZE_1 = 1 val FILTER_SIZE_5 = 2 val FILTER_SIZE_OVER_5 = 3 + + val SORT_ASC_SIZE = 0 + val SORT_DESC_SIZE = 1 + val SORT_ASC_DATE = 2 + val SORT_DESC_DATE = 3 } + private var sortDialogFragment: SortDialogFragment? = null private var columns = 3 private var dateAdapter: PhotoDisplayDateAdapter? = null - private var sizeAdapter: PhotoDisplayDateChildAdapter? = null - val selectedSet = mutableSetOf() // 保存选中状态 + + //文件大小排序使用的适配器 + private var sizeSortAdapter: PhotoDisplayDateChildAdapter? = null //默认倒序排序 @@ -55,12 +68,26 @@ class PhotoSortingActivity : BaseActivity() { //筛选大小,默认全部-1 private var filterSize = FILTER_SIZE_ALL + private var filterDatePopupWindows: FilterPopupWindows? = null + private var filterSizePopupWindows: FilterPopupWindows? = null + private var filterLayoutPopupWindows: FilterPopupWindows? = null + private lateinit var sortBySizeBigToSmall: List private lateinit var sortBySizeSmallToBig: List - private lateinit var sortByDayReverse: List>> - private lateinit var sortedByPositive: List>> + private lateinit var sortByDateReverse: List>> + private lateinit var sortedByDatePositive: List>> + + //选中的所有数据集合(实际选中) + private lateinit var allSelectedSetList: Set + + //选中的所有数据集合(筛选后的数据实际显示的所有选中) + private lateinit var filterSelectedSetList: Set + private lateinit var mItemDecoration: GridSpacingItemDecoration + + + private lateinit var viewModel: ScanRepository override fun inflateBinding(inflater: LayoutInflater): ActivityPhotoSortingBinding = ActivityPhotoSortingBinding.inflate(inflater) @@ -69,32 +96,57 @@ class PhotoSortingActivity : BaseActivity() { val list: ArrayList? = intent.getParcelableArrayListExtraCompat(KEY_PHOTO_FOLDER_FILE) + mItemDecoration = + GridSpacingItemDecoration(columns, Common.itemSpacing, Common.horizontalSpacing) + updateButtonCounts(0) + viewModel = ViewModelProvider(this).get(ScanRepository::class.java) + + viewModel.selectedLiveData.observe(this) { selectedSet -> + allSelectedSetList = selectedSet + Common.showLog("所有数据 选中状态更新: ${selectedSet.size}") + } + + viewModel.selectedDisplayLiveData.observe(this) { displaySet -> + filterSelectedSetList = displaySet + Common.showLog("当前显示筛选数据 选中状态更新: ${displaySet.size}") + updateCurrentIsAllSelectStatus() + } list?.let { - //倒序(最近的在前面) - sortByDayReverse = Common.getSortByDayNewToOld(it) - //正序(最远的在前面) - sortedByPositive = Common.getSortByDayOldToNew(sortByDayReverse) + + //降序(最近的在前面) + sortByDateReverse = Common.getSortByDayNewToOld(it) + //升序(时间最远的在前面) + sortedByDatePositive = Common.getSortByDayOldToNew(sortByDateReverse) sortBySizeBigToSmall = Common.getSortBySizeBigToSmall(it) sortBySizeSmallToBig = Common.getSortBySizeSmallToBig(it) - sizeAdapter = PhotoDisplayDateChildAdapter(this@PhotoSortingActivity) - dateAdapter = PhotoDisplayDateAdapter(this@PhotoSortingActivity).apply { - setData(sortByDayReverse) + sizeSortAdapter = PhotoDisplayDateChildAdapter( + this@PhotoSortingActivity, + columns, viewModel + ) { path, isAdd, allSelected -> +// updateSelectedList(isAdd, path) +// updateCurrentIsAllSelectStatus() + viewModel.toggleSelection(isAdd, path) } + dateAdapter = + PhotoDisplayDateAdapter( + this@PhotoSortingActivity, + columns, + viewModel + ) { actionPath, isAdd -> +// updateSelectedList(isAdd, actionPath) +// updateCurrentIsAllSelectStatus() + viewModel.toggleSelection(isAdd, actionPath) + }.apply { + setData(sortByDateReverse) + } setDateAdapter() setFilter() binding.run { - - lifecycleScope.launch { - ScanRepository.instance.selectedFlow.collect { newSet -> - println("选中集合变化:$newSet") - tvSelectCounts.text = newSet.size.toString() - } - } - tvSelectCounts.setOnClickListener { + tvRecover.setOnClickListener { lifecycleScope.copySelectedFilesAsync( - selectedSet = ScanRepository.instance.selectedFlow.value, + selectedSet = filterSelectedSetList, folder = Common.recoveryPhotoDir, onProgress = { currentCounts: Int, fileName: String, success: Boolean -> @@ -109,10 +161,10 @@ class PhotoSortingActivity : BaseActivity() { } } - btnDelete.setOnClickListener { + tvDelete.setOnClickListener { lifecycleScope.deleteFilesAsync( - selectedSet = ScanRepository.instance.selectedFlow.value, - onProgress = { currentCounts: Int, path:String, success: Boolean -> + selectedSet = filterSelectedSetList, + onProgress = { currentCounts: Int, path: String, success: Boolean -> ScanManager.showLog( "--------删除图片 ", "----------${currentCounts} ${path}" @@ -124,136 +176,203 @@ class PhotoSortingActivity : BaseActivity() { } } - linear.setOnCheckedChangeListener { group, checkedId -> - when (checkedId) { - R.id.btn_date_old_to_new -> { - setDateAdapter() - dateAdapter?.setData(sortedByPositive) - sortReverse = false - } + imSort.setOnClickListener { + sortDialogFragment = sortDialogFragment ?: SortDialogFragment { + when (it) { + SORT_ASC_DATE -> { + setDateAdapter() + dateAdapter?.setData(sortedByDatePositive) + sortReverse = false + } - R.id.btn_date_new_to_old -> { - setDateAdapter() - dateAdapter?.setData(sortByDayReverse) - sortReverse = true - } + SORT_DESC_DATE -> { + setDateAdapter() + dateAdapter?.setData(sortByDateReverse) + sortReverse = true + } - R.id.btn_size_big_to_small -> { - setSizeAdapter() - sizeAdapter?.setData(sortBySizeBigToSmall) - sortReverse = true - } + SORT_DESC_SIZE -> { + setSizeAdapter() + sizeSortAdapter?.setData(sortBySizeBigToSmall) + sortReverse = true + } - R.id.btn_size_small_to_big -> { - setSizeAdapter() - sizeAdapter?.setData(sortBySizeSmallToBig) - sortReverse = false + SORT_ASC_SIZE -> { + setSizeAdapter() + sizeSortAdapter?.setData(sortBySizeSmallToBig) + sortReverse = false + } } } - - + sortDialogFragment?.show(supportFragmentManager, "") } - imSelectAll.setOnClickListener { - imSelectAll.isSelected = !imSelectAll.isSelected - sizeAdapter?.setAllSelected(imSelectAll.isSelected) - dateAdapter?.setAllSelected(imSelectAll.isSelected) - } + //全选按钮 只对当前显示的数据有效 + tvSelectAll.setOnClickListener { + it.isSelected = !it.isSelected + dateAdapter?.setAllSelected(it.isSelected) + sizeSortAdapter?.setAllSelected(it.isSelected) + } } - - } } + private fun updateCurrentIsAllSelectStatus() { + filterSelectedSetList.size.let { + binding.tvSelectCounts.text = it.toString() + updateButtonCounts(it) + when (binding.recyclerView.adapter) { + is PhotoDisplayDateAdapter -> { + val adapter = binding.recyclerView.adapter as PhotoDisplayDateAdapter + binding.tvSelectAll.isSelected = it == adapter.getTotalChildCount() + } + + is PhotoDisplayDateChildAdapter -> { + binding.tvSelectAll.isSelected = it == binding.recyclerView.adapter?.itemCount + } + } + } + } + private fun setDateAdapter() { binding.recyclerView.run { adapter = dateAdapter?.apply { setColumns(columns) } layoutManager = LinearLayoutManager(this@PhotoSortingActivity) + val bPx = 16.dpToPx(context) + setPadding(0, 0, 0, 0) + clipToPadding = false + + } } private fun setSizeAdapter() { binding.recyclerView.run { - adapter = sizeAdapter + val aPx = 16.dpToPx(context) + val bPx = 6.dpToPx(context) + setPadding(aPx, 0, bPx, 0) + clipToPadding = false + layoutManager = GridLayoutManager(context, columns) - val gridSpacingItemDecoration = - GridSpacingItemDecoration(4, 8.dpToPx(this@PhotoSortingActivity), true) - addItemDecorationOnce(gridSpacingItemDecoration) +// resetItemDecorationOnce(mItemDecoration) + adapter = sizeSortAdapter + } } /** - * 筛选 + * 筛选和布局设置 */ private fun setFilter() { - binding.linearDate.setOnCheckedChangeListener { group, checkedId -> - when (checkedId) { - R.id.btn_date_all -> { - filterDate = FILTER_DATE_ALL + //日期筛选 + binding.run { + filterDateLayout.setOnClickListener { + setItemSelect(it as LinearLayout, true) + resources.getStringArray(R.array.filter_date).let { data -> + filterDatePopupWindows = filterDatePopupWindows ?: FilterPopupWindows( + this@PhotoSortingActivity, + data, + 0, + { clickValue -> + when (clickValue) { + data[0] -> filterDate = FILTER_DATE_ALL + data[1] -> filterDate = FILTER_DATE_1 + data[2] -> filterDate = FILTER_DATE_6 + data[3] -> filterDate = FILTER_DATE_24 + data[4] -> {} + } + startFilter() + }) { + setItemSelect(it, false) + } + + filterDatePopupWindows?.show(it) } - R.id.btn_date_within_1 -> { - filterDate = FILTER_DATE_1 - } - R.id.btn_date_customize -> { - - } } - startFilter() + //大小筛选 + filterSizeLayout.setOnClickListener { + setItemSelect(it as LinearLayout, true) + resources.getStringArray(R.array.filter_size).let { data -> + filterSizePopupWindows = filterSizePopupWindows ?: FilterPopupWindows( + this@PhotoSortingActivity, + data, + 0, + { clickValue -> + when (clickValue) { + data[0] -> filterSize = FILTER_SIZE_ALL + data[1] -> filterSize = FILTER_SIZE_1 + data[2] -> filterSize = FILTER_SIZE_5 + data[3] -> filterSize = FILTER_SIZE_OVER_5 + } + startFilter() + }) { + setItemSelect(it, false) + } + filterSizePopupWindows?.show(it) + + } + + + } + + //布局设置 + filterLayoutLinearlayout.setOnClickListener { + setItemSelect(it as LinearLayout, true) + resources.getStringArray(R.array.filter_layout).let { data -> + filterLayoutPopupWindows = filterLayoutPopupWindows ?: FilterPopupWindows( + this@PhotoSortingActivity, + data, + 1, + { clickValue -> + when (clickValue) { + data[0] -> columns = 2 + data[1] -> columns = 3 + data[2] -> columns = 4 + } + when (binding.recyclerView.adapter) { + is PhotoDisplayDateAdapter -> { + dateAdapter?.setColumns(columns) + } + + is PhotoDisplayDateChildAdapter -> { + binding.recyclerView.layoutManager = + GridLayoutManager(this@PhotoSortingActivity, columns) + sizeSortAdapter?.setColumns(columns) + } + } + }) { + setItemSelect(it, false) + } + } + filterLayoutPopupWindows?.show(it) + + } + + } - binding.linearSize.setOnCheckedChangeListener { group, checkedId -> - when (checkedId) { - R.id.btn_size_all -> { - filterSize = FILTER_SIZE_ALL - } + } - R.id.btn_size_1m -> { - filterSize = FILTER_SIZE_1 - } - R.id.btn_size_5m -> { - filterSize = FILTER_SIZE_5 - } - - R.id.btn_size_over_5m -> { - filterSize = FILTER_SIZE_OVER_5 - } + /** + * 根据当前显示的实际数据选中的数量来更新按钮文字和状态 + */ + private fun updateButtonCounts(selectedCounts: Int) { + binding.run { + if (selectedCounts <= 0) { + tvDelete.isEnabled = false + tvRecover.isEnabled = false + } else { + tvDelete.isEnabled = true + tvRecover.isEnabled = true } - startFilter() + tvDelete.text = getString(R.string.delete_placeholder, selectedCounts) + tvRecover.text = getString(R.string.recover_placeholder, selectedCounts) } - binding.rgLayout.setOnCheckedChangeListener { group, checkedId -> - - when (checkedId) { - R.id.rb_columns_2 -> { - columns = 2 - } - - R.id.rb_columns_3 -> { - columns = 3 - } - - R.id.rb_columns_4 -> { - columns = 4 - } - - } - - when (binding.recyclerView.adapter) { - is PhotoDisplayDateAdapter -> { - dateAdapter?.setColumns(columns) - } - - is PhotoDisplayDateChildAdapter -> { - binding.recyclerView.layoutManager = - GridLayoutManager(this@PhotoSortingActivity, columns) - - } - } - } } @@ -262,33 +381,41 @@ class PhotoSortingActivity : BaseActivity() { */ private fun startFilter() { when (binding.recyclerView.adapter) { + //当前是时间排序 is PhotoDisplayDateAdapter -> { - val list = if (sortReverse) sortByDayReverse else sortedByPositive + //确定当前排序 + val list = if (sortReverse) sortByDateReverse else sortedByDatePositive val filterSizeCovert = filterSizeCovert(filterSize) list.filterWithinMonths(filterDate) - .filterBySize(filterSizeCovert.first, filterSizeCovert.second).let { - dateAdapter?.setData(it) - ScanManager.showLog( - "---", - "---date-----${it.size} filterDate=${filterDate} first=${filterSizeCovert.first} second=${filterSizeCovert.second} dateAdapter=${dateAdapter}" - ) + .filterBySize(filterSizeCovert.first, filterSizeCovert.second) + .let { currentList -> + //对筛选后的数据与实际选中集合对比 ,得出筛选后显示的数据中的选中数据 + val checkSelectListContain = + Common.checkSelectListContainDate(currentList, allSelectedSetList) + updateButtonCounts(checkSelectListContain.first) + viewModel.filterResetDisplayFlow(checkSelectListContain.second) + + Common.showLog( "筛选后重置 allSelectedSetList=${allSelectedSetList.size} filterSelectedSetList=${filterSelectedSetList.size} Thread=${Thread.currentThread().name}") + dateAdapter?.resetAllValue(null) + dateAdapter?.setData(currentList) + } } - + //当前是大小排序 is PhotoDisplayDateChildAdapter -> { val list = if (sortReverse) sortBySizeBigToSmall else sortBySizeSmallToBig val filterSizeCovert = filterSizeCovert(filterSize) list.filterWithinMonthsList(filterDate) - .filterBySizeList(filterSizeCovert.first, filterSizeCovert.second).let { - sizeAdapter?.setData(it) - ScanManager.showLog( - "---", - "----size----${it.size} filterDate=${filterDate} first=${filterSizeCovert.first} second=${filterSizeCovert.second} sizeAdapter=${sizeAdapter}" - ) + .filterBySizeList(filterSizeCovert.first, filterSizeCovert.second) + .let { currentList -> + //对筛选后的数据与实际选中集合对比 ,得出筛选后显示的数据中的选中数据 + val checkSelectListContain = + Common.checkSelectListContainSize(currentList, allSelectedSetList) + updateButtonCounts(checkSelectListContain.first) + viewModel.filterResetDisplayFlow(checkSelectListContain.second) + sizeSortAdapter?.setData(currentList) } - - } } } @@ -306,4 +433,5 @@ class PhotoSortingActivity : BaseActivity() { } + } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotosFiles.kt b/app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotosFiles.kt index 8cdc0ba..5545853 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotosFiles.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/photo/ResultPhotosFiles.kt @@ -11,7 +11,7 @@ data class ResultPhotosFiles( val path: String?= null, val size: Long, // 字节 val lastModified: Long, // 时间戳 - var isSelected: Boolean = false // 选中状态 +// var isSelected: Boolean = false // 选中状态 ): Parcelable{ val targetFile: File? get() = path?.let { File(it) } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/photo/SortDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/photo/SortDialogFragment.kt new file mode 100644 index 0000000..14dabe3 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/photo/SortDialogFragment.kt @@ -0,0 +1,116 @@ +package com.ux.video.file.filerecovery.photo + +import android.graphics.Color +import android.os.Bundle +import android.view.Gravity +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.graphics.drawable.toDrawable +import androidx.fragment.app.DialogFragment +import com.ux.video.file.filerecovery.R +import com.ux.video.file.filerecovery.databinding.CommonLayoutSortItemBinding +import com.ux.video.file.filerecovery.databinding.DialogSortBinding + + +class SortDialogFragment(val onClickSort: (type: Int) -> Unit) : DialogFragment() { + + private lateinit var binding: DialogSortBinding + private var clickType = PhotoSortingActivity.SORT_DESC_DATE + + private lateinit var LayoutList: List + override fun onStart() { + super.onStart() + dialog?.window?.apply { + setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + setGravity(Gravity.BOTTOM) + setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogSortBinding.inflate(inflater) + binding.run { + LayoutList = listOf( + ascendingSizeLayout, + descendingSizeLayout, + ascendingDateLayout, + descendingDateLayout + ) + setMutualExclusion(LayoutList) + resetSelectedSortType(clickType) + setIconText( + LayoutList[0], + R.drawable.icon_ascending_size, + getString(R.string.size), + getString(R.string.asc_size_describe) + ) + setIconText( + LayoutList[1], + R.drawable.icon_descending_size, + getString(R.string.size), + getString(R.string.desc_size_describe) + ) + setIconText( + LayoutList[2], + R.drawable.icon_ascending_date, + getString(R.string.date), + getString(R.string.asc_date_describe) + ) + setIconText( + LayoutList[3], + R.drawable.icon_descending_date, + getString(R.string.date), + getString(R.string.desc_date_describe) + ) + + tvCancel.setOnClickListener { dismiss() } + tvOk.setOnClickListener { + onClickSort(clickType) + dismiss() + } + } + return binding.root + } + + + fun resetSelectedSortType(type: Int) { + LayoutList.forEachIndexed { index, item -> + item.checkImage.isSelected = index == type + } + } + + + private fun setIconText( + itemBinding: CommonLayoutSortItemBinding, + iconId: Int, + text: String, + subText: String + ) { + itemBinding.run { + sortType.text = text + sortSubType.text = subText + icon.setImageResource(iconId) + } + } + + private fun setMutualExclusion(listOf: List) { + for (i in 0 until listOf.size) { + val binding = listOf[i] + binding.root.setOnClickListener { + for (j in 0 until listOf.size) { + listOf[j].checkImage.isSelected = false + } + clickType = i + binding.checkImage.isSelected = true + } + } + + } +} diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ExitDialogFragment.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ExitDialogFragment.kt new file mode 100644 index 0000000..bdcf522 --- /dev/null +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ExitDialogFragment.kt @@ -0,0 +1,42 @@ +package com.ux.video.file.filerecovery.result + +import android.graphics.Color +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.graphics.drawable.toDrawable +import androidx.fragment.app.DialogFragment +import com.ux.video.file.filerecovery.databinding.DialogExitBinding +import com.ux.video.file.filerecovery.databinding.DialogPermissionBinding + + +class ExitDialogFragment(val onClickExit: () -> Unit) : DialogFragment() { + + private lateinit var binding: DialogExitBinding + override fun onStart() { + super.onStart() + dialog?.window?.apply { + setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = DialogExitBinding.inflate(inflater) + binding.run { + cancel.setOnClickListener { dismiss() } + exit.setOnClickListener { + onClickExit() + dismiss() + } + } + return binding.root + } +} diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultAdapter.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultAdapter.kt index 37201d5..3951f76 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultAdapter.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultAdapter.kt @@ -1,5 +1,6 @@ package com.ux.video.file.filerecovery.result +import android.annotation.SuppressLint import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup @@ -27,15 +28,19 @@ class ScanResultAdapter( false ) + @SuppressLint("SetTextI18n") override fun bindItem( holder: VHolder, item: ResultPhotos ) { holder.vb.run { + val imageViews = listOf(im1, im2, im3) item.run { - textDirNameCount.text = "$dirName(${allFiles.size})" + imageViewEnter.setOnClickListener { onClickItem(allFiles) } + textDirNameCount.text = dirName + textFileCounts.text = "(${allFiles.size})" val takeFiles = allFiles.take(3) imageViews.forEachIndexed { index, imageView -> if (index < takeFiles.size) { @@ -59,7 +64,7 @@ class ScanResultAdapter( .load(file) .apply( RequestOptions() - .transform(CenterCrop(), RoundedCorners(15.dpToPx(context))) + .transform(CenterCrop(), RoundedCorners(8.dpToPx(context))) ) .into(imageView) } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt index 6dcb1fc..2fec3f1 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanResultDisplayActivity.kt @@ -2,13 +2,27 @@ package com.ux.video.file.filerecovery.result import android.content.Intent import android.view.LayoutInflater +import android.view.View +import androidx.activity.OnBackPressedCallback +import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager +import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.base.BaseActivity import com.ux.video.file.filerecovery.databinding.ActivityScanResultDisplayBinding import com.ux.video.file.filerecovery.photo.PhotoSortingActivity import com.ux.video.file.filerecovery.photo.ResultPhotos import com.ux.video.file.filerecovery.result.ScanningActivity +import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video import com.ux.video.file.filerecovery.utils.ExtendFunctions.getParcelableArrayListExtraCompat +import com.ux.video.file.filerecovery.utils.ScanManager import com.ux.video.file.filerecovery.utils.ScanRepository /** @@ -16,9 +30,12 @@ import com.ux.video.file.filerecovery.utils.ScanRepository */ class ScanResultDisplayActivity : BaseActivity() { private var scanResultAdapter: ScanResultAdapter? = null + private var scanType: Int = VALUE_SCAN_TYPE_photo + private var exitDialog: ExitDialogFragment? = null companion object { val KEY_SCAN_RESULT = "scan_result" + } override fun inflateBinding(inflater: LayoutInflater): ActivityScanResultDisplayBinding = @@ -28,10 +45,17 @@ class ScanResultDisplayActivity : BaseActivity super.initView() val list: ArrayList? = intent.getParcelableArrayListExtraCompat(KEY_SCAN_RESULT) - scanResultAdapter = ScanResultAdapter(this@ScanResultDisplayActivity){ folderLists-> - startActivity(Intent(this@ScanResultDisplayActivity, PhotoSortingActivity::class.java).apply { - putParcelableArrayListExtra(PhotoSortingActivity.KEY_PHOTO_FOLDER_FILE, folderLists) - }) + scanResultAdapter = ScanResultAdapter(this@ScanResultDisplayActivity) { folderLists -> + startActivity( + Intent( + this@ScanResultDisplayActivity, + PhotoSortingActivity::class.java + ).apply { + putParcelableArrayListExtra( + PhotoSortingActivity.KEY_PHOTO_FOLDER_FILE, + folderLists + ) + }) } binding.recyclerResult.run { adapter = scanResultAdapter @@ -56,4 +80,50 @@ class ScanResultDisplayActivity : BaseActivity // scanResultAdapter?.setData(it) // } } + + override fun initData() { + super.initData() + scanType = intent.getIntExtra(KEY_SCAN_TYPE, VALUE_SCAN_TYPE_photo) + setSelectTypeTitle(scanType) + binding.imageViewBack.setOnClickListener { dealExit() } + + onBackPressedDispatcher.addCallback( + this, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + dealExit() + } + }) + } + + + private fun dealExit(){ + exitDialog = exitDialog?:ExitDialogFragment(){ + finish() + } + exitDialog?.show(supportFragmentManager,"") + } + private fun setSelectTypeTitle(fileType: Int) { + binding.run { + when (fileType) { + VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_deleted_photo -> { + title.text = getString(R.string.photo_title) + } + + VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_deleted_video -> { + title.text = getString(R.string.video_title) + } + + VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_deleted_audio -> { + title.text = getString(R.string.audio_title) + } + + VALUE_SCAN_TYPE_documents, VALUE_SCAN_TYPE_deleted_documents -> { + title.text = getString(R.string.document_title) + } + } + } + + + } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivity.kt b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivity.kt index 9ae5876..73d0cfa 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivity.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/result/ScanningActivity.kt @@ -11,6 +11,15 @@ import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALU import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_DOCUMENT import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_PHOTO import com.ux.video.file.filerecovery.main.ScanSelectTypeActivity.Companion.VALUE_VIDEO +import com.ux.video.file.filerecovery.utils.Common.KEY_SCAN_TYPE +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video import com.ux.video.file.filerecovery.utils.ScanManager import com.ux.video.file.filerecovery.utils.ScanRepository import com.ux.video.file.filerecovery.utils.ScanState @@ -21,15 +30,15 @@ import kotlinx.coroutines.launch class ScanningActivity : BaseActivity() { companion object { - val KEY_SCAN_TYPE = "scan_type" - val VALUE_SCAN_TYPE_photo = 0 - val VALUE_SCAN_TYPE_deleted_photo = 1 - val VALUE_SCAN_TYPE_video = 2 - val VALUE_SCAN_TYPE_deleted_video = 3 - val VALUE_SCAN_TYPE_audio = 4 - val VALUE_SCAN_TYPE_deleted_audio = 5 - val VALUE_SCAN_TYPE_documents = 6 - val VALUE_SCAN_TYPE_deleted_documents = 7 +// val KEY_SCAN_TYPE = "scan_type" +// val VALUE_SCAN_TYPE_photo = 0 +// val VALUE_SCAN_TYPE_deleted_photo = 1 +// val VALUE_SCAN_TYPE_video = 2 +// val VALUE_SCAN_TYPE_deleted_video = 3 +// val VALUE_SCAN_TYPE_audio = 4 +// val VALUE_SCAN_TYPE_deleted_audio = 5 +// val VALUE_SCAN_TYPE_documents = 6 +// val VALUE_SCAN_TYPE_deleted_documents = 7 } private var scanType: Int = VALUE_SCAN_TYPE_photo @@ -44,7 +53,7 @@ class ScanningActivity : BaseActivity() { VALUE_SCAN_TYPE_photo, VALUE_SCAN_TYPE_video, VALUE_SCAN_TYPE_audio, VALUE_SCAN_TYPE_documents -> scanAll() VALUE_SCAN_TYPE_deleted_photo, VALUE_SCAN_TYPE_deleted_video, VALUE_SCAN_TYPE_deleted_audio, VALUE_SCAN_TYPE_deleted_documents -> scanDeleted() } - binding.scanProgress.setCenterImage(R.drawable.ic_scan_file) + binding.scanProgress.setCenterImage(R.drawable.im_photo_center_image) binding.imageViewBack.setOnClickListener { finish() } } @@ -176,6 +185,7 @@ class ScanningActivity : BaseActivity() { ) } + finish() } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/CircleImageProgressView.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/CircleImageProgressView.kt index d5dba7c..00f12f6 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/CircleImageProgressView.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/CircleImageProgressView.kt @@ -7,6 +7,7 @@ import android.graphics.BitmapFactory import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint +import android.graphics.Path import android.graphics.RectF import android.util.AttributeSet import android.view.View @@ -19,7 +20,7 @@ class CircleImageProgressView @JvmOverloads constructor( private val paintBg = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE strokeWidth = 20f - color = Color.parseColor("#C9DDEF") // 背景浅蓝 + color = Color.parseColor("#9CC0EC") // 背景浅蓝 } private val paintProgress = Paint(Paint.ANTI_ALIAS_FLAG).apply { @@ -57,44 +58,42 @@ class CircleImageProgressView @JvmOverloads constructor( override fun onDraw(canvas: Canvas) { super.onDraw(canvas) + val stroke = paintBg.strokeWidth - val rect = RectF(stroke, stroke, width - stroke, height - stroke) + val radius = (width / 2f) - (stroke / 2f) - // 背景圆环 + val rect = RectF( + (width / 2f) - radius, + (height / 2f) - radius, + (width / 2f) + radius, + (height / 2f) + radius + ) + + // 1. 背景圆环 canvas.drawArc(rect, 0f, 360f, false, paintBg) - // 中心图片 + + // 2. 中心图片(圆形裁剪) centerBitmap?.let { -// val left = (width - it.width) / 2f -// val top = (height - it.height) / 2f -// canvas.drawBitmap(it, left, top, null) + val innerRadius = radius - stroke / 2f // 图片只占内圆,不盖住圆环 -// val desiredSize = width * imageScale -// val rectDst = RectF( -// (width - desiredSize) / 2f, -// (height - desiredSize) / 2f, -// (width + desiredSize) / 2f, -// (height + desiredSize) / 2f -// ) -// canvas.drawBitmap(it, null, rectDst, null) - - val innerRadius = (width / 2f) - stroke // 圆环内半径 val left = (width / 2f) - innerRadius val top = (height / 2f) - innerRadius val right = (width / 2f) + innerRadius val bottom = (height / 2f) + innerRadius - val rectDst = RectF(left, top, right, bottom) - // 缩放图片填满内圆区域 -// canvas.drawBitmap(it, null, rectDst, null) + val saveCount = canvas.save() + val path = Path().apply { + addCircle(width / 2f, height / 2f, innerRadius, Path.Direction.CW) + } + canvas.clipPath(path) + canvas.drawBitmap(it, null, rectDst, null) + canvas.restoreToCount(saveCount) } - - - // 进度圆弧 + // 3. 进度圆弧 val sweepAngle = progress * 360f / 100f canvas.drawArc(rect, -90f, sweepAngle, false, paintProgress) - } } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/Common.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/Common.kt index af59f33..f77e4a9 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/Common.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/Common.kt @@ -4,19 +4,38 @@ import android.content.Context import android.icu.text.SimpleDateFormat import android.icu.util.Calendar import android.os.Environment +import android.util.Log +import android.view.ViewGroup +import com.ux.video.file.filerecovery.App import com.ux.video.file.filerecovery.R import com.ux.video.file.filerecovery.photo.ResultPhotosFiles +import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx import java.util.Date import java.util.Locale import kotlin.collections.sortedBy object Common { + + val itemSpacing = 10 + val horizontalSpacing = 16 + + val KEY_SCAN_TYPE = "scan_type" + val VALUE_SCAN_TYPE_photo = 0 + val VALUE_SCAN_TYPE_deleted_photo = 1 + val VALUE_SCAN_TYPE_video = 2 + val VALUE_SCAN_TYPE_deleted_video = 3 + val VALUE_SCAN_TYPE_audio = 4 + val VALUE_SCAN_TYPE_deleted_audio = 5 + val VALUE_SCAN_TYPE_documents = 6 + val VALUE_SCAN_TYPE_deleted_documents = 7 + + val rootDir = Environment.getExternalStorageDirectory() - val dateFormat = SimpleDateFormat("MMM dd yyyy", Locale.ENGLISH) - val recoveryPhotoDir = "MyAllRecovery/Photo" + val dateFormat = SimpleDateFormat("MMMM d,yyyy", Locale.ENGLISH) + val recoveryPhotoDir = "MyAllRecovery/Photo" /** - * 默认按照日期分类,将最新的排前面 + * 默认按照日期分类,将最新的排前面 降序 */ fun getSortByDayNewToOld(list: ArrayList): List>> { @@ -31,21 +50,21 @@ object Common { } /** - * 按照日期排序, 时间最早的排前面 + * 按照日期排序, 时间最早的排前面 升序 * */ fun getSortByDayOldToNew(list: List>>) = list.sortedBy { dateFormat.parse(it.first)?.time ?: 0L } /** - * 按照文件大小排序,将最大的排前面 + * 按照文件大小排序,将最大的排前面 降序 */ fun getSortBySizeBigToSmall(list: ArrayList) = list.sortedByDescending { it.size } /** - * 按照文件大小排序,将最大的排前面 + * 按照文件大小排序,将最小的排前面 升序 */ fun getSortBySizeSmallToBig(list: ArrayList) = list.sortedBy { it.size @@ -77,7 +96,7 @@ object Common { * @param months 筛选months月之内的数据 */ fun filterWithinOneMonthByDay( - grouped: List>>,months: Int + grouped: List>>, months: Int ): List>> { val today = Calendar.getInstance() val oneMonthAgo = Calendar.getInstance().apply { @@ -89,10 +108,11 @@ object Common { day != null && !day.before(oneMonthAgo.time) && !day.after(today.time) } } + /** * @param months 筛选months月之内的数据 */ - fun filterWithinOneMonth(list: List,months: Int): List { + fun filterWithinOneMonth(list: List, months: Int): List { val today = Calendar.getInstance() val oneMonthAgo = Calendar.getInstance().apply { add(Calendar.MONTH, -months) @@ -104,4 +124,55 @@ object Common { } } + /** + * 设置全部子View的选中 + */ + fun setItemSelect(view: ViewGroup, boolean: Boolean) { + for (i in 0 until view.childCount) { + val child = view.getChildAt(i) + child.isSelected = boolean + } + } + + + + + /** + * @param list 筛选后的显示的数据 + * @param selected 实际选中的数据集合 + * @return 显示的选中数量和选中集合 + */ + fun checkSelectListContainDate( + list: List>>, + selected: Set + ): Pair> { + val currentSelected = mutableSetOf() + + val totalSelectedCount = list.sumOf { pair -> + pair.second.count { + val isSelected = it.path in selected + if (isSelected) currentSelected.add(it.path.toString()) + isSelected + } + } + + showLog("-------totalSelectedCount = $totalSelectedCount currentSelected=${currentSelected.size}") + return totalSelectedCount to currentSelected + } + + + fun checkSelectListContainSize(list: List, selected: Set): Pair> { + val currentSelected = mutableSetOf() + val totalSelectedCount = list.count { + val isSelected = it.path in selected + if (isSelected) currentSelected.add(it.path.toString()) + isSelected + } + showLog("-------totalSelectedCount 222=${totalSelectedCount}") + return totalSelectedCount to currentSelected + } + + fun showLog(msg: String) { + Log.d("============", msg) + } } \ No newline at end of file diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/CustomTextView.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/CustomTextView.kt index 2a365b9..bbeeb85 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/CustomTextView.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/CustomTextView.kt @@ -17,10 +17,15 @@ class CustomTextView @JvmOverloads constructor( private var alimama: Typeface? = null } + private var selectedText: String? = null + private var normalText: String? = null + init { attrs?.let { val typedArray = context.obtainStyledAttributes(it, R.styleable.CustomTextView) val type = typedArray.getInt(R.styleable.CustomTextView_fontType, 0) + selectedText = typedArray.getString(R.styleable.CustomTextView_selected_text) + normalText = typedArray.getString(R.styleable.CustomTextView_normal_text) typedArray.recycle() Typeface.create("sans-serif-light", Typeface.NORMAL) // Roboto Light @@ -31,9 +36,10 @@ class CustomTextView @JvmOverloads constructor( 0 -> { typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) } + 1 -> { - typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD) + typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) } 2 -> { @@ -43,12 +49,19 @@ class CustomTextView @JvmOverloads constructor( typeface = alimama } - 3 -> { - typeface = Typeface.create("sans-serif-medium", Typeface.NORMAL) - } else -> typeface = Typeface.DEFAULT } } } + + override fun setSelected(selected: Boolean) { + super.setSelected(selected) + updateText() + } + + private fun updateText() { +// Common.showLog("--updateText:$isSelected selectedText=${selectedText} normalText=${normalText} text=${text}") + text = if (isSelected) selectedText ?: text else normalText ?: text + } } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt index ef6e30d..676e568 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ExtendFunctions.kt @@ -29,11 +29,9 @@ object ExtendFunctions { } } - fun RecyclerView.addItemDecorationOnce(decoration: RecyclerView.ItemDecoration) { - for (i in 0 until itemDecorationCount) { - if (getItemDecorationAt(i)::class == decoration::class) { - return // 已经有同类型,避免重复 - } + fun RecyclerView.resetItemDecorationOnce(decoration: RecyclerView.ItemDecoration) { + while (itemDecorationCount > 0) { + removeItemDecorationAt(0) } addItemDecoration(decoration) } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/GridSpacingItemDecoration.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/GridSpacingItemDecoration.kt index 6ce22e0..7e2b760 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/GridSpacingItemDecoration.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/GridSpacingItemDecoration.kt @@ -3,11 +3,19 @@ package com.ux.video.file.filerecovery.utils import android.graphics.Rect import android.view.View import androidx.recyclerview.widget.RecyclerView +import com.ux.video.file.filerecovery.App +import com.ux.video.file.filerecovery.utils.ExtendFunctions.dpToPx + +/** + * @param spanCount 列数 + * @param spacing 列与列之间的间距(dp) + * @param edgeSpacing RecyclerView 左右两边的边距(dp) + */ class GridSpacingItemDecoration( private val spanCount: Int, - private val spacing: Int, - private val includeEdge: Boolean + private val itemSpacing: Int, + private val edgeSpacing: Int ) : RecyclerView.ItemDecoration() { override fun getItemOffsets( @@ -16,20 +24,31 @@ class GridSpacingItemDecoration( val position = parent.getChildAdapterPosition(view) val column = position % spanCount - if (includeEdge) { - outRect.left = spacing - column * spacing / spanCount - outRect.right = (column + 1) * spacing / spanCount + val itemSpacingPx = itemSpacing.dpToPx(App.mAppContext) + val edgePx = edgeSpacing.dpToPx(App.mAppContext) - if (position < spanCount) { - outRect.top = spacing + when (column) { + 0 -> { // 第一列 + outRect.left = edgePx + outRect.right = itemSpacingPx / 2 } - outRect.bottom = spacing - } else { - outRect.left = column * spacing / spanCount - outRect.right = spacing - (column + 1) * spacing / spanCount - if (position >= spanCount) { - outRect.top = spacing + spanCount - 1 -> { // 最后一列 + outRect.left = itemSpacingPx / 2 + outRect.right = edgePx + } + else -> { // 中间列 + outRect.left = itemSpacingPx / 2 + outRect.right = itemSpacingPx / 2 } } + + // 顶部间距(第一行也加) + if (position < spanCount) { + outRect.top = itemSpacingPx + } + // 底部间距 + outRect.bottom = itemSpacingPx } } + + diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt index 57f5747..efe790b 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanManager.kt @@ -6,6 +6,14 @@ import android.util.Log import com.ux.video.file.filerecovery.photo.ResultPhotos import com.ux.video.file.filerecovery.photo.ResultPhotosFiles import com.ux.video.file.filerecovery.result.ScanningActivity +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_audio +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_audio +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_documents +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_photo +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_deleted_video +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_documents +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_photo +import com.ux.video.file.filerecovery.utils.Common.VALUE_SCAN_TYPE_video import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow @@ -56,19 +64,19 @@ object ScanManager { } else { var fileCheckBoolean: Boolean = false when (type) { - ScanningActivity.VALUE_SCAN_TYPE_photo -> { + VALUE_SCAN_TYPE_photo -> { fileCheckBoolean = isFormatFile(file, IMAGE_FILE) && isValidImage(file) } - ScanningActivity.VALUE_SCAN_TYPE_video -> { + VALUE_SCAN_TYPE_video -> { fileCheckBoolean = isFormatFile(file, VIDEO_FILE) } - ScanningActivity.VALUE_SCAN_TYPE_audio -> { + VALUE_SCAN_TYPE_audio -> { fileCheckBoolean = isFormatFile(file, AUDIO_FILE) } - ScanningActivity.VALUE_SCAN_TYPE_documents -> { + VALUE_SCAN_TYPE_documents -> { fileCheckBoolean = file.isFile && isFormatFile(file, DOCUMENT_FILE) } } @@ -126,20 +134,20 @@ object ScanManager { if (insideHidden) { var fileCheckBoolean: Boolean = false when (type) { - ScanningActivity.VALUE_SCAN_TYPE_deleted_photo -> { + VALUE_SCAN_TYPE_deleted_photo -> { fileCheckBoolean = isFormatFile(file, IMAGE_FILE) && isValidImage(file) } - ScanningActivity.VALUE_SCAN_TYPE_deleted_video -> { + VALUE_SCAN_TYPE_deleted_video -> { fileCheckBoolean = isFormatFile(file, VIDEO_FILE) } - ScanningActivity.VALUE_SCAN_TYPE_deleted_audio -> { + VALUE_SCAN_TYPE_deleted_audio -> { fileCheckBoolean = isFormatFile(file, AUDIO_FILE) } - ScanningActivity.VALUE_SCAN_TYPE_deleted_documents -> { + VALUE_SCAN_TYPE_deleted_documents -> { fileCheckBoolean = file.isFile && isFormatFile(file, DOCUMENT_FILE) } } diff --git a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt index 5fa5695..999c10d 100644 --- a/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt +++ b/app/src/main/java/com/ux/video/file/filerecovery/utils/ScanRepository.kt @@ -2,49 +2,78 @@ package com.ux.video.file.filerecovery.utils import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel import com.ux.video.file.filerecovery.photo.ResultPhotos import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -class ScanRepository private constructor() { - - //---------扫描结果 - private val _photoResults = MutableLiveData>() - val photoResults: LiveData> get() = _photoResults - - private val _videoResults = MutableLiveData>() - val videoResults: LiveData> get() = _videoResults +class ScanRepository : ViewModel() { +// private val _selectedFlow = MutableStateFlow>(mutableSetOf()) +// val selectedFlow: StateFlow> = _selectedFlow +// +// +// private val _selectedDisplayFlow = MutableStateFlow>(mutableSetOf()) +// val selectedDisplayFlow: StateFlow> = _selectedDisplayFlow - //----------查看指定目录里面的文件 - fun setPhotoResults(data: List) { - _photoResults.value = data - } + private val _selectedLiveData = MutableLiveData>(emptySet()) + val selectedLiveData: LiveData> = _selectedLiveData - fun setVideoResults(data: List) { - _videoResults.value = data - } + // 当前筛选显示的选中项 + private val _selectedDisplayLiveData = MutableLiveData>(emptySet()) + val selectedDisplayLiveData: LiveData> = _selectedDisplayLiveData - private val _selectedFlow = MutableStateFlow>(emptySet()) - val selectedFlow: StateFlow> = _selectedFlow + fun toggleSelection(isAdd: Boolean, path: String) { + val current = _selectedLiveData.value?.toMutableSet() ?: mutableSetOf() + val currentDisplay = _selectedDisplayLiveData.value?.toMutableSet() ?: mutableSetOf() - fun toggleSelection(boolean: Boolean, path: String) { - val current = _selectedFlow.value.toMutableSet() - if (boolean) { + if (isAdd) { current.add(path) - ScanManager.showLog("_------", "add selected ${path}") + currentDisplay.add(path) +// Common.showLog( "add selected ${path}") } else { current.remove(path) - ScanManager.showLog("_------", "remove selected ${path}") + currentDisplay.remove(path) +// Common.showLog( "remove selected ${path}") } - _selectedFlow.value = current + Common.showLog( "toggleSelection------------ _selectedDisplayFlow=${_selectedDisplayLiveData.value?.size} _selectedFlow=${_selectedLiveData.value?.size} ") + // LiveData 使用新对象赋值,保证 observer 触发 + _selectedLiveData.value = current.toSet() + _selectedDisplayLiveData.value = currentDisplay.toSet() } - fun checkIsSelect(path: String) : Boolean{ - val current = _selectedFlow.value.toMutableSet() - return current.contains(path) +// fun toggleSelection(isAdd: Boolean, path: String) { +// val current = _selectedFlow.value.toMutableSet() +// val currentDisplay = _selectedDisplayFlow.value.toMutableSet() +// if (isAdd) { +// current.add(path) +// currentDisplay.add(path) +// Common.showLog( "add selected ${path}") +// } else { +// current.remove(path) +// currentDisplay.remove(path) +// Common.showLog( "remove selected ${path}") +// } +// _selectedFlow.value = current +// _selectedDisplayFlow.value = currentDisplay +// } + + /** + * 数据筛选后重置当前显示的选中集合 + */ + fun filterResetDisplayFlow(list: MutableSet){ + _selectedDisplayLiveData.value = list.toSet() + + Common.showLog( "筛选后重置 _selectedDisplayFlow=${_selectedDisplayLiveData.value?.size} _selectedFlow=${_selectedLiveData.value?.size} ") + } + + + + fun checkIsSelect(path: String): Boolean { + val current = _selectedDisplayLiveData.value + return current?.contains(path) == true } diff --git a/app/src/main/res/color/selector_black_blue.xml b/app/src/main/res/color/selector_black_blue.xml new file mode 100644 index 0000000..b02353b --- /dev/null +++ b/app/src/main/res/color/selector_black_blue.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_c7c7cc_blue.xml b/app/src/main/res/color/selector_c7c7cc_blue.xml new file mode 100644 index 0000000..f208423 --- /dev/null +++ b/app/src/main/res/color/selector_c7c7cc_blue.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_delete_button_enable.xml b/app/src/main/res/color/selector_delete_button_enable.xml new file mode 100644 index 0000000..da652f9 --- /dev/null +++ b/app/src/main/res/color/selector_delete_button_enable.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/color/selector_recover_button_enable.xml b/app/src/main/res/color/selector_recover_button_enable.xml new file mode 100644 index 0000000..da652f9 --- /dev/null +++ b/app/src/main/res/color/selector_recover_button_enable.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/color/selector_switch_thumb_color.xml b/app/src/main/res/color/selector_switch_thumb_color.xml new file mode 100644 index 0000000..32915a7 --- /dev/null +++ b/app/src/main/res/color/selector_switch_thumb_color.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/color/selector_switch_track_color.xml b/app/src/main/res/color/selector_switch_track_color.xml new file mode 100644 index 0000000..fc7f5ec --- /dev/null +++ b/app/src/main/res/color/selector_switch_track_color.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/app/src/main/res/drawable/bg_dialog_btn_allow_solid_8.xml b/app/src/main/res/drawable/bg_dialog_btn_allow_solid_8.xml index 3fe8798..b122147 100644 --- a/app/src/main/res/drawable/bg_dialog_btn_allow_solid_8.xml +++ b/app/src/main/res/drawable/bg_dialog_btn_allow_solid_8.xml @@ -2,6 +2,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_dialog_btn_cancel_stoke_8.xml b/app/src/main/res/drawable/bg_dialog_btn_cancel_stoke_8.xml index 63cb687..d091084 100644 --- a/app/src/main/res/drawable/bg_dialog_btn_cancel_stoke_8.xml +++ b/app/src/main/res/drawable/bg_dialog_btn_cancel_stoke_8.xml @@ -2,6 +2,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_rectangle_blue_right_8.xml b/app/src/main/res/drawable/bg_rectangle_blue_right_8.xml new file mode 100644 index 0000000..416d06a --- /dev/null +++ b/app/src/main/res/drawable/bg_rectangle_blue_right_8.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_rectangle_d5ebff_right_8.xml b/app/src/main/res/drawable/bg_rectangle_d5ebff_right_8.xml new file mode 100644 index 0000000..2e907cf --- /dev/null +++ b/app/src/main/res/drawable/bg_rectangle_d5ebff_right_8.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_rectangle_f5f5fa_left_8.xml b/app/src/main/res/drawable/bg_rectangle_f5f5fa_left_8.xml new file mode 100644 index 0000000..98b7434 --- /dev/null +++ b/app/src/main/res/drawable/bg_rectangle_f5f5fa_left_8.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_dialog_permission_white_20.xml b/app/src/main/res/drawable/bg_rectangle_white_20.xml similarity index 100% rename from app/src/main/res/drawable/bg_dialog_permission_white_20.xml rename to app/src/main/res/drawable/bg_rectangle_white_20.xml diff --git a/app/src/main/res/drawable/bg_rectangle_white_bottom_20.xml b/app/src/main/res/drawable/bg_rectangle_white_bottom_20.xml new file mode 100644 index 0000000..ab5d04f --- /dev/null +++ b/app/src/main/res/drawable/bg_rectangle_white_bottom_20.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_rectangle_white_left_8.xml b/app/src/main/res/drawable/bg_rectangle_white_left_8.xml new file mode 100644 index 0000000..0ebe77e --- /dev/null +++ b/app/src/main/res/drawable/bg_rectangle_white_left_8.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_rectangle_white_top_20.xml b/app/src/main/res/drawable/bg_rectangle_white_top_20.xml new file mode 100644 index 0000000..9587995 --- /dev/null +++ b/app/src/main/res/drawable/bg_rectangle_white_top_20.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/icon_arrow_down_gray.png b/app/src/main/res/drawable/icon_arrow_down_gray.png new file mode 100644 index 0000000000000000000000000000000000000000..5f901da96c55f52b5639b2928eb98ebe23bcf21c GIT binary patch literal 436 zcmV;l0ZaagP)u1LpkjiGVZcZwv$PY$_%#X&vYq%Zh=`+D$H)Gj3zQe} z#Lo>UW%qeJp8u}ZeLvdl$~U;v35;s1O1xf6ZT0Yvc+ex|S!dnn9}d5*!S%grY=>&3 z?DQdfr({POn6UC!tLnqxg!fq%=z|R$gR3gF8yfa|8Q>hO@5SEWHkb~JSQr)yN3%3A1%|_kW1|!> z6^6l!V>1_+10#PD$3QAD7e@9hj-gm!2^hISI0i$3rC?+R;}{MB>N)cP)o<&^N1i*Z z4NoHq#~oZw2Q3}<7`MB})e5W-l86tkQOnH|q0;kbY>LRn80Gt5rS`1X=S literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_arrow_up_blue.png b/app/src/main/res/drawable/icon_arrow_up_blue.png new file mode 100644 index 0000000000000000000000000000000000000000..50b82715ec5af9e7b0f92d6e75f6768c74815ebb GIT binary patch literal 552 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 Y0Qq6p;q5nUeRTu8J&x1wx^dI~0gqAp2><{9 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_checkmark_16dp.xml b/app/src/main/res/drawable/icon_checkmark_16dp.xml new file mode 100644 index 0000000..e72314f --- /dev/null +++ b/app/src/main/res/drawable/icon_checkmark_16dp.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/app/src/main/res/drawable/icon_checkmark_28dp.xml b/app/src/main/res/drawable/icon_checkmark_28dp.xml new file mode 100644 index 0000000..618c31e --- /dev/null +++ b/app/src/main/res/drawable/icon_checkmark_28dp.xml @@ -0,0 +1,16 @@ + + + + + + + diff --git a/app/src/main/res/drawable/icon_checkmark_file.png b/app/src/main/res/drawable/icon_checkmark_file.png new file mode 100644 index 0000000000000000000000000000000000000000..bbe81e62833bbaaab876efbff7217e2abb2cdb25 GIT binary patch literal 458 zcmV;*0X6=KP)R5*=|lS@hiK@f(&8Unfy95b^KAHfquPY}e7ZoGpMLBXK6 zaIb5f=`oheUiZGv#7TY(v!fp`fX*@29D%7JB72r^(4GFo#kA zK&X>&Ns`;c4Bgn}@_p1n^5Hf@8L{)0lmy|3V0azm-7?Std~*g;>I?Qrpr*m_Jjj~^ zfFJH=h0}*n!Vk9r9NcyhWjPC2m=Jch1{oU_P|CQ?=^Re9hTvp@ua1Y992ZVbBOV?j z!I4!5VYXA?<@@)}hRd6;oY$iC!eI_P^MP-)aUW~F5o=*4RD9Eo!c3_3Myzq1=u+k7 z#~q&e=o&0lUNnxAA0~goT?60TbrQBoX|eS;oW?EqrfMfUCaU@$tmI8KU0u&4y(X>J zKd@(F+;%Z-d*1Bhu##mLEFBWKE9v~7TN@(Ova?zvXjalm0x=PWQqTGaq9h_s3sxwgwc0fk~1JSLpSM*si-07*qoM6N<$f|&`# A%K!iX literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_checkmark_filter.png b/app/src/main/res/drawable/icon_checkmark_filter.png new file mode 100644 index 0000000000000000000000000000000000000000..c946327d2bc5e9b7c563bac686e3a3bd6fd764eb GIT binary patch literal 648 zcmV;30(bq1P)? zO;W-z7>3_w>53JOJC5KDlUB=aR~5N10cAEhc>hGrwn@pD1f&|fFgK11So_zUw~qGvju2?-xknYwBf%E!RBGyFl0e3(F9Mn zXcu6YRA2^(0oXhgUvu=AS1iyTjqqd>@ZURt48ym-X~2_H?GiM@lU6`_$xm9Rgc{%@ z0iL(vATc^5)C5mvfgpI{AT=r_)Cf;zfl1gKlk$mgQlmmb&G6)nc7eNQ2f8H~0RJa| zBb^cqfhTsf0ytuHNYD&Vth8(kV5Le4hQSkeR{%d%A;Ca+qV5;KGgV436rPyL0(iCx z2?oOxFCO#mzNNbr}>Kya`n9oB?>`@^kke!Z$>7r+^0 zl>m9k^7I6?!*hQ51n`Q?5?GuRGvGP9`~rAYDGBDl6Q!5{URg+jS@6UtE`V3(l3*S@ z5y}bR9WqHU6Q0vAFMxMRm0&JBr(TT!-iZ?6J#ZLj1BqLV>)q$$xg@^)00030|1fUR iyZ`_I21!IgR09BIB8pTjIUD@|0000u literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_descending_date.png b/app/src/main/res/drawable/icon_descending_date.png new file mode 100644 index 0000000000000000000000000000000000000000..d229ee815b53c1f6eae632ba785e79d36ebd35a3 GIT binary patch literal 2428 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_descending_size.png b/app/src/main/res/drawable/icon_descending_size.png new file mode 100644 index 0000000000000000000000000000000000000000..fe5ab3352c5fb75f0d671135e3034f3020cd8fc6 GIT binary patch literal 1248 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/icon_selected.xml b/app/src/main/res/drawable/icon_selected.xml deleted file mode 100644 index d175613..0000000 --- a/app/src/main/res/drawable/icon_selected.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/app/src/main/res/drawable/icon_sort.png b/app/src/main/res/drawable/icon_sort.png new file mode 100644 index 0000000000000000000000000000000000000000..8c7c9cccaed45d46b0033a1a3f261a7ae36dac4d GIT binary patch literal 950 zcmV;n14;aeP) zh#Gf;!PQyCKDp}B)(zT(Nq#HsRt82#h|D{)IC__t-`_0Uayc19w0Ypw?hS|EeBZmY zdA1U;bl{_WRZnawrRneerGiopH?!5Owf_znu+01^+o}KEFQu?nScvg#;C_GnUZb*r zg*^*+Fc`Oq=o}Tb+Qz_VXL!caSFNCr{oV)|urFUWZXPRX{`(}bz%~I(WcEz%;E9y0 zQhhBjV0U&J^0s3NU~S7<{$&&$g}{%n!jgLdFb2xn*2zgck?X3`O5k;{@<^I8+e3}8 zP$mZ~uzQ%saxteYGf$1NtxhMtm+LCi67U@M1@6W<*g2j3pcxhnOTeP+$QWL^re#iJ zfmL-!24KdF1I98>F1{7ZC7H^fLx2UC17@a`ORqU>TivP%aI4G#6LIcY8yYMIzyXW% zMa-oTVFBlWljQh3O{oP?W9YEc31=D`?{L5<>voTB0h%O7*YHEMCEySNvrIK`c0@w+ zYlTBoG|N*foE?$S{957A6wUI~3THhFZA9hH)Wu0P zusm{P^PUPe>G^(l*R}xZn_6JNB7V$=S>$;F*f@@-i!x)3I4Xe!wkgloA5<7DXIraa zg;(D%9)DpA0&jRiWY@-zsZyBPK-to;>J*M5YD=e=-i^gK@@6%CN?Cjkrb=OSIBS44 zX8knw>!=4NiV#1xab%!sUaaAH{c@B<*pPHc_|Rjj#Q7tt|5pLxyWr ztTHgZTsqQ|JLWztNsen+a07*qoM6N<$f{ + android:fillColor="@color/black"/> diff --git a/app/src/main/res/drawable/icon_unselected_28dp.xml b/app/src/main/res/drawable/icon_unselected_28dp.xml new file mode 100644 index 0000000..1600cfb --- /dev/null +++ b/app/src/main/res/drawable/icon_unselected_28dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/icon_vector.png b/app/src/main/res/drawable/icon_vector.png new file mode 100644 index 0000000000000000000000000000000000000000..c512922a4a70c0440b738c630d07ddaa7a70450f GIT binary patch literal 379 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/imag_no_photo.png b/app/src/main/res/drawable/imag_no_photo.png new file mode 100644 index 0000000000000000000000000000000000000000..37d7393fb7b6958864bf770e5208f13c8b057f21 GIT binary patch literal 26393 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/photo_size_bg.xml b/app/src/main/res/drawable/photo_size_bg.xml index aecf70b..3067255 100644 --- a/app/src/main/res/drawable/photo_size_bg.xml +++ b/app/src/main/res/drawable/photo_size_bg.xml @@ -1,7 +1,7 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_arrow_up_down.xml b/app/src/main/res/drawable/selector_arrow_up_down.xml new file mode 100644 index 0000000..f24ce1f --- /dev/null +++ b/app/src/main/res/drawable/selector_arrow_up_down.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_filter_image_view.xml b/app/src/main/res/drawable/selector_filter_image_view.xml new file mode 100644 index 0000000..dea1331 --- /dev/null +++ b/app/src/main/res/drawable/selector_filter_image_view.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_icon_checkmark_28dp.xml b/app/src/main/res/drawable/selector_icon_checkmark_28dp.xml new file mode 100644 index 0000000..24e71cc --- /dev/null +++ b/app/src/main/res/drawable/selector_icon_checkmark_28dp.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_icon_select.xml b/app/src/main/res/drawable/selector_icon_select.xml deleted file mode 100644 index c3dd063..0000000 --- a/app/src/main/res/drawable/selector_icon_select.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_icon_select_16.xml b/app/src/main/res/drawable/selector_icon_select_16.xml new file mode 100644 index 0000000..a17cba6 --- /dev/null +++ b/app/src/main/res/drawable/selector_icon_select_16.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_recover_button_enable.xml b/app/src/main/res/drawable/selector_recover_button_enable.xml new file mode 100644 index 0000000..0c463cd --- /dev/null +++ b/app/src/main/res/drawable/selector_recover_button_enable.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_photo_sorting.xml b/app/src/main/res/layout/activity_photo_sorting.xml index 33042ef..967bec9 100644 --- a/app/src/main/res/layout/activity_photo_sorting.xml +++ b/app/src/main/res/layout/activity_photo_sorting.xml @@ -1,169 +1,353 @@ - - - - -