This commit is contained in:
2026-06-30 10:00:31 +09:00
parent 0400385cf8
commit dab91b1a65
5 changed files with 107 additions and 18 deletions
+1 -1
View File
@@ -130,7 +130,7 @@
android:name=".player.DocumentViewerActivity" android:name=".player.DocumentViewerActivity"
android:theme="@style/Theme.Player" android:theme="@style/Theme.Player"
android:launchMode="singleInstance" android:launchMode="singleInstance"
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize" android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|density|fontScale"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:excludeFromRecents="false" android:excludeFromRecents="false"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
@@ -499,6 +499,7 @@ class CompletedFilesFragment : Fragment() {
FileViewMode.GRID_SMALL -> "apps" FileViewMode.GRID_SMALL -> "apps"
} }
} }
val MAX_COUNT = 9
private fun setupControls(view: View) { private fun setupControls(view: View) {
val layoutTitleDefault = view.findViewById<View>(R.id.layoutTitleDefault) val layoutTitleDefault = view.findViewById<View>(R.id.layoutTitleDefault)
val layoutTitleSearch = view.findViewById<View>(R.id.layoutTitleSearch) val layoutTitleSearch = view.findViewById<View>(R.id.layoutTitleSearch)
@@ -577,8 +578,8 @@ class CompletedFilesFragment : Fragment() {
} }
val message = if (protectedCount > 5) { val message = if (protectedCount > MAX_COUNT) {
Toast.makeText(context, "보호된 폴더 내부는 5개 이하만 동시 삭제 가능함.", Toast.LENGTH_SHORT).show() Toast.makeText(context, "보호된 폴더 내부는 ${MAX_COUNT}개 이하만 동시 삭제 가능함.", Toast.LENGTH_SHORT).show()
return@setOnClickListener return@setOnClickListener
} else { } else {
"선택한 ${selectedFiles.size}개 항목을 삭제하시겠습니까?" "선택한 ${selectedFiles.size}개 항목을 삭제하시겠습니까?"
@@ -160,11 +160,13 @@ class PagedTextLayout : ConstraintLayout , PagedTextGenerateInterface {
} }
var currentPageTextView : TextView? = null var currentPageTextView : TextView? = null
var currentChapter : TextView? = null
fun initView(context: Context) { fun initView(context: Context) {
inflate(context, R.layout.layout_textviewer, this) inflate(context, R.layout.layout_textviewer, this)
mainTextView = findViewById(R.id.first_view) mainTextView = findViewById(R.id.first_view)
sencondTextView = findViewById(R.id.sencond_view) sencondTextView = findViewById(R.id.sencond_view)
currentPageTextView = findViewById(R.id.current_page) currentPageTextView = findViewById(R.id.current_page)
currentChapter = findViewById(R.id.current_chapter)
if (mPagedTextViewInterface?.usePageInfo() ?: false) { if (mPagedTextViewInterface?.usePageInfo() ?: false) {
} else { } else {
@@ -29,6 +29,88 @@ class DocumentViewerActivity : AppCompatActivity(), PagedTextViewInterface {
private var encoding: String = "utf-8" private var encoding: String = "utf-8"
private lateinit var pageIndexer: PageIndexer private lateinit var pageIndexer: PageIndexer
private var currentPageIndex = 0 private var currentPageIndex = 0
private var indexingJob: kotlinx.coroutines.Job? = null
// 2. 기존 initializeReader() 함수를 크기 변경 시에도 재사용 가능하도록 리팩토링
private fun initializeReader(targetOffset: Long = -1L) {
// 기존 작동 중인 인덱싱이 있다면 취소
indexingJob?.cancel()
indexingJob = lifecycleScope.launch {
currentFile?.let { file ->
// 인코딩은 최초 1회만 구해도 무방하므로 검증 로직 제외 가능
if (encoding.isEmpty()) encoding = detectFileEncoding(file)
val paint = pagedLayout.mainTextView?.paint ?: return@launch
val targetWidth = (pagedLayout.mainTextView!!.width * 0.8).toInt()
val targetHeight = (pagedLayout.mainTextView!!.height * 0.8).toInt()
// 0 이하의 크기 방어 (뷰가 아직 가로세로 측정이 안 되었을 때)
if (targetWidth <= 0 || targetHeight <= 0) return@launch
pageIndexer = PageIndexer(
file, encoding, paint, targetWidth, targetHeight
)
var hasRestoredPage = false
pageIndexer.buildIndex { progress ->
runOnUiThread {
val currentOffsetsSize = pageIndexer.pageOffsets.size
if (!hasRestoredPage) {
if (targetOffset >= 0L) {
// [크기 재조정 케이스] 기억해둔 오프셋 위치가 확보되었는지 확인
val foundIndex = pageIndexer.pageOffsets.indexOfLast { it <= targetOffset }
// 마지막 오프셋이거나, 다음 페이지 오프셋까지 리스트에 확보되었을 때 전환
if (foundIndex >= 0 && (foundIndex < currentOffsetsSize - 1 || progress >= 100)) {
showPage(foundIndex)
hasRestoredPage = true
}
} else {
// [최초 진입 케이스] SharedPreferences 복구
val savedPageIndex = sharedPreferences.getInt(file.absolutePath, 0)
if (currentOffsetsSize > savedPageIndex) {
showPage(savedPageIndex)
hasRestoredPage = true
}
}
}
pagedLayout.currentPageTextView?.text = "${currentPageIndex + 1} / $currentOffsetsSize"
}
}
}
}
}
// 3. 팝업 크기 변경 감지 콜백 구현
override fun onConfigurationChanged(newConfig: android.content.res.Configuration) {
super.onConfigurationChanged(newConfig)
// 현재 읽고 있던 페이지의 실제 파일 시작 offset을 백업합니다.
val currentOffsetBackup = if (::pageIndexer.isInitialized && currentPageIndex in pageIndexer.pageOffsets.indices) {
pageIndexer.pageOffsets[currentPageIndex]
} else {
-1L
}
// 뷰가 새로운 크기로 완전히 배치(Layout)된 후 재인덱싱을 돌려야 정확한 가로/세로가 나옵니다.
pagedLayout.post {
initializeReader(targetOffset = currentOffsetBackup)
}
}
// 4. onDestroy 스케줄러 취소 안전장치 추가
override fun onDestroy() {
indexingJob?.cancel()
super.onDestroy()
try {
raf.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
// SharedPreferences 정의 (마지막 페이지 저장용) // SharedPreferences 정의 (마지막 페이지 저장용)
private val sharedPreferences by lazy { private val sharedPreferences by lazy {
@@ -151,14 +233,19 @@ class DocumentViewerActivity : AppCompatActivity(), PagedTextViewInterface {
val pageText = String(buffer, Charset.forName(encoding)) val pageText = String(buffer, Charset.forName(encoding))
pagedLayout.text = pageText pagedLayout.text = pageText
pagedLayout.currentPageTextView?.text = "${currentPageIndex + 1} / ${pageIndexer.pageOffsets.size}" pagedLayout.currentPageTextView?.text = "${currentPageIndex + 1} / ${pageIndexer.pageOffsets.size}"
pagedLayout.currentChapter?.text = pageIndexer.getChapterForPage(currentPageIndex)?.title
pagedLayout.currentChapter?.setOnClickListener {
if (!::pageIndexer.isInitialized || pageIndexer.pageOffsets.isEmpty()) return@setOnClickListener
showChapterListDialog()
}
} }
override fun onTouch(touchArea: TouchArea) { override fun onTouch(touchArea: TouchArea) {
if (!::pageIndexer.isInitialized || pageIndexer.pageOffsets.isEmpty()) return // if (!::pageIndexer.isInitialized || pageIndexer.pageOffsets.isEmpty()) return
if (touchArea == TouchArea.Center) { // if (touchArea == TouchArea.Center) {
// showPageSeekDialog() //// showPageSeekDialog()
showChapterListDialog() // showChapterListDialog()
} // }
} }
private fun moveToNextChapter() { private fun moveToNextChapter() {
@@ -302,18 +389,14 @@ class DocumentViewerActivity : AppCompatActivity(), PagedTextViewInterface {
} }
} }
override fun onLongClick() {} override fun onLongClick() {
}
override fun usePageInfo(): Boolean = true override fun usePageInfo(): Boolean = true
override fun onTimeoverTouch() {} override fun onTimeoverTouch() {}
override fun onSwipeDown(count: Int) {} override fun onSwipeDown(count: Int) {}
override fun onSwipeUp(count: Int) {} override fun onSwipeUp(count: Int) {}
override fun onDestroy() {
super.onDestroy()
try {
raf.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
} }
@@ -26,7 +26,10 @@ class PageIndexer(
val pageOffsets = ArrayList<Long>() val pageOffsets = ArrayList<Long>()
val chapters = ArrayList<Chapter>() val chapters = ArrayList<Chapter>()
private val charset = Charset.forName(encoding) private val charset = Charset.forName(encoding)
fun getChapterForPage(currentPageIndex: Int): Chapter? {
// 현재 페이지보다 작거나 같은 시작 페이지를 가진 챕터 중, 가장 마지막 챕터를 찾습니다.
return chapters.lastOrNull { it.pageIndex <= currentPageIndex }
}
private val chapterPattern = Pattern.compile( private val chapterPattern = Pattern.compile(
"(?:제\\s*)?\\d+\\s*[화|장|막|절|편]" "(?:제\\s*)?\\d+\\s*[화|장|막|절|편]"
) )