...
This commit is contained in:
parent
996a75fc3e
commit
a8c7641e69
@ -6,17 +6,22 @@ import android.content.Intent
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.webkit.MimeTypeMap
|
import android.webkit.MimeTypeMap
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.OnBackPressedCallback
|
import androidx.activity.OnBackPressedCallback
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
@ -64,6 +69,14 @@ class NaturalOrderComparator : Comparator<File> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class RenameMode(val label: String) {
|
||||||
|
REPLACE("문자열 제거/치환"),
|
||||||
|
TRUNCATE_AFTER("기준 문자열 이후 제거 (앞 남기기)"),
|
||||||
|
TRUNCATE_BEFORE("기준 문자열 이전 제거 (뒤 남기기)"), // 💡 추가
|
||||||
|
BATCH_NUMBERING("전체 변경 + 넘버링"),
|
||||||
|
SEQUENTIAL("순차적 직접 변경")
|
||||||
|
}
|
||||||
|
|
||||||
class CompletedFilesFragment : Fragment() {
|
class CompletedFilesFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var recyclerView: RecyclerView
|
private lateinit var recyclerView: RecyclerView
|
||||||
@ -77,7 +90,7 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
private var currentSort = FileSortType.NAME
|
private var currentSort = FileSortType.NAME
|
||||||
private var currentViewMode = FileViewMode.LIST_TEXT
|
private var currentViewMode = FileViewMode.LIST_TEXT
|
||||||
private var isDescending = true
|
private var isDescending = true
|
||||||
|
private var searchQuery = "" // 💡 실시간 검색어 저장용 변수
|
||||||
// 💡 멀티 선택 모드 상태 관리 변수
|
// 💡 멀티 선택 모드 상태 관리 변수
|
||||||
private var isSelectionMode = false
|
private var isSelectionMode = false
|
||||||
private val selectedFiles = mutableSetOf<File>()
|
private val selectedFiles = mutableSetOf<File>()
|
||||||
@ -403,15 +416,59 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
private fun setupControls(view: View) {
|
private fun setupControls(view: View) {
|
||||||
|
val layoutTitleDefault = view.findViewById<View>(R.id.layoutTitleDefault)
|
||||||
|
val layoutTitleSearch = view.findViewById<View>(R.id.layoutTitleSearch)
|
||||||
|
val etSearch = view.findViewById<EditText>(R.id.etSearch)
|
||||||
|
val btnSearch = view.findViewById<TextView>(R.id.btnSearch)
|
||||||
|
val btnCloseSearch = view.findViewById<TextView>(R.id.btnCloseSearch)
|
||||||
val spinnerFilter = view.findViewById<Spinner>(R.id.spinnerFilter)
|
val spinnerFilter = view.findViewById<Spinner>(R.id.spinnerFilter)
|
||||||
val spinnerSort = view.findViewById<Spinner>(R.id.spinnerSort)
|
val spinnerSort = view.findViewById<Spinner>(R.id.spinnerSort)
|
||||||
val tvSortOrder = view.findViewById<TextView>(R.id.tvSortOrder)
|
val tvSortOrder = view.findViewById<TextView>(R.id.tvSortOrder)
|
||||||
val btnViewMode = view.findViewById<TextView>(R.id.btnViewMode)
|
val btnViewMode = view.findViewById<TextView>(R.id.btnViewMode)
|
||||||
val btnOrganize = view.findViewById<TextView>(R.id.btnOrganize)
|
val btnOrganize = view.findViewById<TextView>(R.id.btnOrganize)
|
||||||
|
// 💡 검색 모드 진입
|
||||||
|
btnSearch.setOnClickListener {
|
||||||
|
layoutTitleDefault.visibility = View.GONE
|
||||||
|
layoutTitleSearch.visibility = View.VISIBLE
|
||||||
|
etSearch.requestFocus()
|
||||||
|
// 키보드 보이기 로직(선택사항)
|
||||||
|
etSearch.postDelayed({ // 뷰가 완전히 그려진 후 키보드를 띄우기 위해 약간의 지연 실행
|
||||||
|
val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
imm.showSoftInput(etSearch, InputMethodManager.SHOW_IMPLICIT)
|
||||||
|
}, 100)
|
||||||
|
|
||||||
|
}
|
||||||
|
view.findViewById<View>(R.id.tvTitle)?.setOnClickListener { btnSearch.performClick() }
|
||||||
|
|
||||||
|
// 💡 검색 모드 종료
|
||||||
|
btnCloseSearch.setOnClickListener {
|
||||||
|
searchQuery = ""
|
||||||
|
etSearch.setText("")
|
||||||
|
layoutTitleDefault.visibility = View.VISIBLE
|
||||||
|
layoutTitleSearch.visibility = View.GONE
|
||||||
|
applyFilterAndSort() // 리스트 복구
|
||||||
|
}
|
||||||
|
|
||||||
|
// 💡 실시간 타이핑 감지
|
||||||
|
etSearch.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
searchQuery = s.toString()
|
||||||
|
applyFilterAndSort() // 텍스트 바뀔 때마다 즉시 필터링
|
||||||
|
}
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||||
|
})
|
||||||
|
|
||||||
// 💡 선택 액션 바 버튼 이벤트
|
// 💡 선택 액션 바 버튼 이벤트
|
||||||
|
view.findViewById<View>(R.id.btnSelectAll)?.setOnClickListener {
|
||||||
|
selectedFiles.clear()
|
||||||
|
selectedFiles.addAll(adapter.getAll())
|
||||||
|
adapter.updateSelection(selectedFiles)
|
||||||
|
}
|
||||||
|
|
||||||
view.findViewById<View>(R.id.btnCancelSelection)?.setOnClickListener { toggleSelectionMode(false) }
|
view.findViewById<View>(R.id.btnCancelSelection)?.setOnClickListener { toggleSelectionMode(false) }
|
||||||
view.findViewById<View>(R.id.btnMoveSelected)?.setOnClickListener { showMoveDialog() }
|
view.findViewById<View>(R.id.btnMoveSelected)?.setOnClickListener { showMoveDialog() }
|
||||||
|
view.findViewById<View>(R.id.btnRenameSelected)?.setOnClickListener { showBatchRenameDialog() }
|
||||||
view.findViewById<View>(R.id.btnDeleteSelected)?.setOnClickListener {
|
view.findViewById<View>(R.id.btnDeleteSelected)?.setOnClickListener {
|
||||||
if (selectedFiles.isEmpty()) return@setOnClickListener
|
if (selectedFiles.isEmpty()) return@setOnClickListener
|
||||||
android.app.AlertDialog.Builder(requireContext())
|
android.app.AlertDialog.Builder(requireContext())
|
||||||
@ -495,6 +552,188 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun renamePrefsEntry(oldName: String, newName: String) {
|
||||||
|
val prefs = requireContext().getSharedPreferences("FileAccessTracker", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
// 1. 기존 데이터 가져오기
|
||||||
|
val lastAccessed = prefs.getLong(oldName, 0L)
|
||||||
|
val accessCount = prefs.getInt("${oldName}_count", 0)
|
||||||
|
|
||||||
|
// 2. 새 이름으로 데이터 저장 및 기존 데이터 삭제
|
||||||
|
if (lastAccessed != 0L || accessCount != 0) {
|
||||||
|
prefs.edit().apply {
|
||||||
|
// 새 키로 복사
|
||||||
|
putLong(newName, lastAccessed)
|
||||||
|
putInt("${newName}_count", accessCount)
|
||||||
|
// 기존 키 삭제
|
||||||
|
remove(oldName)
|
||||||
|
remove("${oldName}_count")
|
||||||
|
apply()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun generateNewName(file: File, mode: RenameMode, in1: String, in2: String, index: Int): String {
|
||||||
|
val extension = file.extension
|
||||||
|
val fileNameOnly = file.nameWithoutExtension
|
||||||
|
|
||||||
|
return when (mode) {
|
||||||
|
RenameMode.REPLACE -> {
|
||||||
|
// 특정 문자열 제거(in2가 비어있을 때) 또는 치환
|
||||||
|
fileNameOnly.replace(in1, in2) + if (extension.isNotEmpty()) ".$extension" else ""
|
||||||
|
}
|
||||||
|
RenameMode.TRUNCATE_AFTER -> {
|
||||||
|
// 예: "P2024_이미지.jpg"에서 "_" 기준 이후 제거 -> "P2024.jpg"
|
||||||
|
val idx = fileNameOnly.indexOf(in1)
|
||||||
|
if (idx != -1) fileNameOnly.substring(0, idx) + if (extension.isNotEmpty()) ".$extension" else ""
|
||||||
|
else file.name
|
||||||
|
}
|
||||||
|
RenameMode.TRUNCATE_BEFORE -> {
|
||||||
|
// 💡 예: "P2024_이미지.jpg"에서 "_" 기준 이전 제거 -> "이미지.jpg"
|
||||||
|
val idx = fileNameOnly.indexOf(in1)
|
||||||
|
if (idx != -1) {
|
||||||
|
// 키워드 자체도 지우고 싶다면 idx + in1.length 부터 시작
|
||||||
|
fileNameOnly.substring(idx + in1.length) + if (extension.isNotEmpty()) ".$extension" else ""
|
||||||
|
} else file.name
|
||||||
|
}
|
||||||
|
RenameMode.BATCH_NUMBERING -> {
|
||||||
|
// 전체 네이밍 + 넘버링 (예: 여행_01.jpg)
|
||||||
|
"${in1}_${String.format("%02d", index)}" + if (extension.isNotEmpty()) ".$extension" else ""
|
||||||
|
}
|
||||||
|
RenameMode.SEQUENTIAL -> {
|
||||||
|
// 이 모드는 다이얼로그에서 하나씩 입력받는 구조로 별도 처리가 필요할 수 있음
|
||||||
|
"순차변경 대기중"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showSequentialRename(files: List<File>, index: Int) {
|
||||||
|
if (index >= files.size) {
|
||||||
|
toggleSelectionMode(false)
|
||||||
|
loadFiles()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = files[index]
|
||||||
|
val et = EditText(requireContext()).apply {
|
||||||
|
setText(file.name)
|
||||||
|
setSelection(file.nameWithoutExtension.length) // 이름 부분만 드래그/포커스
|
||||||
|
}
|
||||||
|
|
||||||
|
android.app.AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("이름 직접 수정 (${index + 1}/${files.size})")
|
||||||
|
.setView(et)
|
||||||
|
.setCancelable(false)
|
||||||
|
.setPositiveButton("다음") { _, _ ->
|
||||||
|
val oldName = file.name
|
||||||
|
val newName = et.text.toString()
|
||||||
|
val dest = File(file.parentFile, newName)
|
||||||
|
|
||||||
|
if (oldName != newName && file.renameTo(dest)) {
|
||||||
|
// 💡 프리퍼런스 키 업데이트
|
||||||
|
renamePrefsEntry(oldName, newName)
|
||||||
|
}
|
||||||
|
showSequentialRename(files, index + 1)
|
||||||
|
}
|
||||||
|
.setNeutralButton("건너뛰기") { _, _ -> showSequentialRename(files, index + 1) }
|
||||||
|
.setNegativeButton("중단", null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showBatchRenameDialog() {
|
||||||
|
val files = selectedFiles.toList().sortedWith(NaturalOrderComparator())
|
||||||
|
if (files.isEmpty()) return
|
||||||
|
|
||||||
|
val view = layoutInflater.inflate(R.layout.dialog_batch_rename, null)
|
||||||
|
val spinner = view.findViewById<Spinner>(R.id.spinnerRenameMode)
|
||||||
|
val et1 = view.findViewById<EditText>(R.id.etInput1)
|
||||||
|
val et2 = view.findViewById<EditText>(R.id.etInput2)
|
||||||
|
val tvPreview = view.findViewById<TextView>(R.id.tvRenamePreview)
|
||||||
|
|
||||||
|
// 모드 리스트 세팅
|
||||||
|
spinner.adapter = ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, RenameMode.values().map { it.label })
|
||||||
|
|
||||||
|
// 실시간 미리보기 업데이트 함수
|
||||||
|
val updateUI = {
|
||||||
|
val mode = RenameMode.values()[spinner.selectedItemPosition]
|
||||||
|
val preview = files.take(3).mapIndexed { i, f ->
|
||||||
|
"${f.name} → ${generateNewName(f, mode, et1.text.toString(), et2.text.toString(), i + 1)}"
|
||||||
|
}.joinToString("\n")
|
||||||
|
tvPreview.text = "미리보기(최대 3개):\n$preview"
|
||||||
|
}
|
||||||
|
|
||||||
|
var tWatcher = object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(
|
||||||
|
s: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
count: Int,
|
||||||
|
after: Int
|
||||||
|
) {
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTextChanged(
|
||||||
|
s: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
before: Int,
|
||||||
|
count: Int
|
||||||
|
) {
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 리스너 등록
|
||||||
|
et1.addTextChangedListener(tWatcher)
|
||||||
|
et2.addTextChangedListener(tWatcher)
|
||||||
|
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, pos: Int, p3: Long) {
|
||||||
|
val mode = RenameMode.values()[pos]
|
||||||
|
// 모드별 가이드 UI 조정
|
||||||
|
et1.visibility = if (mode == RenameMode.SEQUENTIAL) View.GONE else View.VISIBLE
|
||||||
|
et2.visibility = if (mode == RenameMode.REPLACE) View.VISIBLE else View.GONE
|
||||||
|
et1.hint = when(mode) {
|
||||||
|
RenameMode.REPLACE -> "찾을 문자열"
|
||||||
|
RenameMode.TRUNCATE_AFTER, RenameMode.TRUNCATE_BEFORE -> "기준 문자열"
|
||||||
|
RenameMode.BATCH_NUMBERING -> "새 파일명 공통 부분"
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
updateUI()
|
||||||
|
}
|
||||||
|
override fun onNothingSelected(p0: AdapterView<*>?) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
android.app.AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle("${files.size}개 파일 이름 변경")
|
||||||
|
.setView(view)
|
||||||
|
.setPositiveButton("적용") { _, _ ->
|
||||||
|
val mode = RenameMode.values()[spinner.selectedItemPosition]
|
||||||
|
if (mode == RenameMode.SEQUENTIAL) {
|
||||||
|
showSequentialRename(files, 0)
|
||||||
|
} else {
|
||||||
|
var count = 0
|
||||||
|
files.forEachIndexed { i, file ->
|
||||||
|
val oldName = file.name
|
||||||
|
val newName = generateNewName(file, mode, et1.text.toString(), et2.text.toString(), i + 1)
|
||||||
|
val dest = File(file.parentFile, newName)
|
||||||
|
|
||||||
|
if (oldName != newName && !dest.exists() && file.renameTo(dest)) {
|
||||||
|
// 💡 파일명이 성공적으로 바뀌었을 때 프리퍼런스 정보도 갱신
|
||||||
|
renamePrefsEntry(oldName, newName)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Toast.makeText(context, "$count 개 파일 이름 변경 완료", Toast.LENGTH_SHORT).show()
|
||||||
|
toggleSelectionMode(false)
|
||||||
|
loadFiles()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton("취소", null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
private fun organizeRootFiles() {
|
private fun organizeRootFiles() {
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
// 1. 루트 폴더의 파일 목록 가져오기
|
// 1. 루트 폴더의 파일 목록 가져오기
|
||||||
@ -572,9 +811,27 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun applyFilterAndSort() {
|
private fun applyFilterAndSort() {
|
||||||
val folders = allFiles.filter { it.isDirectory }
|
// 💡 1. 데이터 소스 결정
|
||||||
var files = allFiles.filter { it.isFile }
|
val baseFiles = if (searchQuery.isNotEmpty()) {
|
||||||
|
// 검색어가 있으면 rootDir부터 모든 하위 파일/폴더를 가져옴 (Recursive)
|
||||||
|
rootDir.walkTopDown().toList()
|
||||||
|
} else {
|
||||||
|
// 검색어가 없으면 현재 폴더(currentDir)의 파일만 대상으로 함
|
||||||
|
allFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 폴더와 파일 분리 및 검색어 필터링
|
||||||
|
var folders = baseFiles.filter { it.isDirectory }
|
||||||
|
var files = baseFiles.filter { it.isFile }
|
||||||
|
|
||||||
|
if (searchQuery.isNotEmpty()) {
|
||||||
|
// 파일명에 검색어가 포함된 것만 추출 (ignoreCase로 대소문자 무시)
|
||||||
|
files = files.filter { it.name.contains(searchQuery, ignoreCase = true) }
|
||||||
|
// 검색 모드일 때는 폴더 결과는 제외하거나 원하면 아래 주석 해제
|
||||||
|
folders = emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 기존 카테고리 필터 적용 (IMAGE, VIDEO 등)
|
||||||
files = files.filter { file ->
|
files = files.filter { file ->
|
||||||
val ext = file.extension.lowercase()
|
val ext = file.extension.lowercase()
|
||||||
when (currentFilter) {
|
when (currentFilter) {
|
||||||
@ -586,6 +843,7 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 정렬 로직 (기존 동일)
|
||||||
val naturalComparator = NaturalOrderComparator()
|
val naturalComparator = NaturalOrderComparator()
|
||||||
val sortedFolders = folders.sortedWith(naturalComparator)
|
val sortedFolders = folders.sortedWith(naturalComparator)
|
||||||
val sortedFiles = when (currentSort) {
|
val sortedFiles = when (currentSort) {
|
||||||
@ -596,8 +854,14 @@ class CompletedFilesFragment : Fragment() {
|
|||||||
FileSortType.NAME -> if (isDescending) files.sortedWith(naturalComparator.reversed()) else files.sortedWith(naturalComparator)
|
FileSortType.NAME -> if (isDescending) files.sortedWith(naturalComparator.reversed()) else files.sortedWith(naturalComparator)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 5. 최종 리스트 구성
|
||||||
val finalItems = mutableListOf<File>()
|
val finalItems = mutableListOf<File>()
|
||||||
if (currentDir.absolutePath != rootDir.absolutePath) finalItems.add(File(currentDir, ".."))
|
|
||||||
|
// 💡 검색 중이 아닐 때만 "상위 폴더(..)" 아이템을 추가
|
||||||
|
if (searchQuery.isEmpty() && currentDir.absolutePath != rootDir.absolutePath) {
|
||||||
|
finalItems.add(File(currentDir, ".."))
|
||||||
|
}
|
||||||
|
|
||||||
finalItems.addAll(sortedFolders)
|
finalItems.addAll(sortedFolders)
|
||||||
finalItems.addAll(sortedFiles)
|
finalItems.addAll(sortedFiles)
|
||||||
|
|
||||||
@ -654,6 +918,8 @@ class CompletedFilesAdapter(
|
|||||||
|
|
||||||
// 💡 어댑터 내부에 선택된 파일 정보 저장
|
// 💡 어댑터 내부에 선택된 파일 정보 저장
|
||||||
private var selectedFiles: Set<File> = emptySet()
|
private var selectedFiles: Set<File> = emptySet()
|
||||||
|
fun getAll() = fileList
|
||||||
|
|
||||||
|
|
||||||
fun submitList(files: List<File>) {
|
fun submitList(files: List<File>) {
|
||||||
fileList = files
|
fileList = files
|
||||||
|
|||||||
51
app/src/main/res/layout/dialog_batch_rename.xml
Normal file
51
app/src/main/res/layout/dialog_batch_rename.xml
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="20dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="변경 모드 선택"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textColor="#888888"
|
||||||
|
android:layout_marginBottom="4dp"/>
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatSpinner
|
||||||
|
android:id="@+id/spinnerRenameMode"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginBottom="16dp"
|
||||||
|
android:background="@android:drawable/btn_dropdown"
|
||||||
|
android:spinnerMode="dropdown" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etInput1"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="입력 1"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_marginBottom="8dp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etInput2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="입력 2"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:layout_marginBottom="16dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvRenamePreview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="#F5F5F5"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:text="미리보기 결과가 여기에 표시됩니다."
|
||||||
|
android:textColor="#555555"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:lineSpacingExtra="4dp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@ -4,28 +4,69 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:background="?android:attr/windowBackground">
|
android:background="?android:attr/windowBackground">
|
||||||
<LinearLayout
|
|
||||||
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content">
|
||||||
android:orientation="horizontal"
|
|
||||||
android:paddingHorizontal="16dp"
|
<LinearLayout
|
||||||
android:paddingBottom="8dp"
|
android:id="@+id/layoutTitleDefault"
|
||||||
android:gravity="center_vertical">
|
android:layout_width="match_parent"
|
||||||
<TextView
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="다운로드 보관함"
|
android:orientation="horizontal"
|
||||||
android:textSize="20sp"
|
android:paddingHorizontal="16dp"
|
||||||
android:textStyle="bold"
|
android:paddingBottom="8dp"
|
||||||
android:padding="16dp"/>
|
android:gravity="center_vertical">
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnOrganize"
|
android:id="@+id/tvTitle"
|
||||||
style="@style/MaterialIconButtonStyle"
|
android:layout_weight="1"
|
||||||
android:layout_width="48dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="wrap_content"
|
||||||
android:text="auto_awesome" />
|
android:text="다운로드 보관함"
|
||||||
</LinearLayout>
|
android:textSize="20sp"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:padding="16dp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnSearch"
|
||||||
|
style="@style/MaterialIconButtonStyle"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="search" />
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnOrganize"
|
||||||
|
style="@style/MaterialIconButtonStyle"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="auto_awesome" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/layoutTitleSearch"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="16dp"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:visibility="gone">
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/etSearch"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="파일명 키워드 검색..."
|
||||||
|
android:background="@null"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:imeOptions="actionSearch"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnCloseSearch"
|
||||||
|
style="@style/MaterialIconButtonStyle"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:text="close" />
|
||||||
|
</LinearLayout>
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -34,50 +75,43 @@
|
|||||||
android:paddingHorizontal="8dp"
|
android:paddingHorizontal="8dp"
|
||||||
android:paddingBottom="8dp"
|
android:paddingBottom="8dp"
|
||||||
android:gravity="center_vertical">
|
android:gravity="center_vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnViewMode"
|
android:id="@+id/btnViewMode"
|
||||||
style="@style/MaterialIconButtonStyle"
|
style="@style/MaterialIconButtonStyle"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:text="view_headline" />
|
android:text="view_headline" />
|
||||||
|
|
||||||
<Space
|
<Space
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"/>
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatSpinner
|
<androidx.appcompat.widget.AppCompatSpinner
|
||||||
android:id="@+id/spinnerFilter"
|
android:id="@+id/spinnerFilter"
|
||||||
android:layout_width="90dp"
|
android:layout_width="90dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:spinnerMode="dropdown"
|
android:spinnerMode="dropdown" />
|
||||||
/>
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.AppCompatSpinner
|
<androidx.appcompat.widget.AppCompatSpinner
|
||||||
android:id="@+id/spinnerSort"
|
android:id="@+id/spinnerSort"
|
||||||
android:layout_width="90dp"
|
android:layout_width="90dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:spinnerMode="dropdown"
|
android:spinnerMode="dropdown" />
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvSortOrder"
|
android:id="@+id/tvSortOrder"
|
||||||
style="@style/MaterialIconButtonStyle"
|
style="@style/MaterialIconButtonStyle"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:text="arrow_downward" />
|
android:text="arrow_downward" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerViewCompletedFiles"
|
android:id="@+id/recyclerViewCompletedFiles"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:padding="8dp"/>
|
android:padding="8dp"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/layoutSelectionActions"
|
android:id="@+id/layoutSelectionActions"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -86,7 +120,15 @@
|
|||||||
android:background="#EEEEEE"
|
android:background="#EEEEEE"
|
||||||
android:padding="8dp"
|
android:padding="8dp"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnSelectAll"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="전체"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="12dp"/>
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnCancelSelection"
|
android:id="@+id/btnCancelSelection"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -96,7 +138,15 @@
|
|||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="12dp"/>
|
android:padding="12dp"/>
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/btnRenameSelected"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:text="이름 변경"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="12dp"/>
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnMoveSelected"
|
android:id="@+id/btnMoveSelected"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
@ -106,7 +156,6 @@
|
|||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:padding="12dp"/>
|
android:padding="12dp"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/btnDeleteSelected"
|
android:id="@+id/btnDeleteSelected"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user