ReaderActivity.kt 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346
  1. package com.kdanmobile.reader
  2. import android.annotation.SuppressLint
  3. import android.app.Activity
  4. import android.app.Dialog
  5. import android.content.Context
  6. import androidx.lifecycle.Observer
  7. import android.content.Intent
  8. import android.net.Uri
  9. import android.os.Build
  10. import android.os.Bundle
  11. import androidx.constraintlayout.widget.ConstraintLayout
  12. import androidx.constraintlayout.widget.ConstraintSet
  13. import com.google.android.material.snackbar.Snackbar
  14. import androidx.core.content.ContextCompat
  15. import androidx.appcompat.app.AlertDialog
  16. import android.util.DisplayMetrics
  17. import android.view.*
  18. import android.view.animation.AnimationUtils
  19. import android.widget.*
  20. import com.kdanmobile.base.KdanBaseActivity
  21. import com.kdanmobile.kmpdfkit.pdfcommon.PDFInfo
  22. import com.kdanmobile.kmpdfkit.manager.KMPDFFactory
  23. import com.kdanmobile.kmpdfkit.pdfcommon.*
  24. import com.kdanmobile.reader.Utils.applyConstraintSet
  25. import com.kdanmobile.reader.additionalpage.*
  26. import com.kdanmobile.reader.annotationattribute.AnnotationAttribute
  27. import com.kdanmobile.reader.annotationattribute.AnnotationColor
  28. import com.kdanmobile.reader.annotationattribute.InkAttribute
  29. import com.kdanmobile.reader.koin.ReadModuleKoinComponent
  30. import com.kdanmobile.reader.password.DialogPasswordActivity
  31. import com.kdanmobile.reader.screen.contextmenu.TextBoxContextMenuActionListener
  32. import com.kdanmobile.reader.screen.model.SharePrefsModel
  33. import com.kdanmobile.reader.screen.reader.mediabox.MediaBoxView
  34. import com.kdanmobile.reader.screen.reader.mediabox.shape.ShapeTabView
  35. import com.kdanmobile.reader.screen.reader.mediabox.signature.SignatureTabView
  36. import com.kdanmobile.reader.screen.reader.mediabox.stamp.StampTabView
  37. import com.kdanmobile.reader.screen.reader.mediabox.textbox.TextBoxTabView
  38. import com.kdanmobile.reader.screen.view.*
  39. import com.kdanmobile.reader.screen.view.SearchView
  40. import com.kdanmobile.reader.setting.ReaderSettingDialogFragment
  41. import com.kdanmobile.reader.setting.ReaderSettingListener
  42. import com.kdanmobile.reader.thumb.PdfThumbDialogFragment
  43. import com.kdanmobile.reader.thumb.PdfThumbFragmentListener
  44. import com.kdanmobile.reader.utils.AnimationUtil
  45. import com.kdanmobile.reader.utils.DensityUtil
  46. import com.kdanmobile.reader.view.AnnotationAttributeView
  47. import com.kdanmobile.reader.view.AnnotationAttributeWindow
  48. import kotlinx.android.synthetic.main.activity_reader.*
  49. import org.koin.android.viewmodel.ext.android.viewModel
  50. import org.koin.core.parameter.parametersOf
  51. import java.io.File
  52. import java.util.*
  53. import kotlin.properties.Delegates
  54. abstract class ReaderActivity :
  55. KdanBaseActivity(),
  56. ReaderSettingListener,
  57. PdfThumbFragmentListener,
  58. ReadModuleKoinComponent {
  59. /**
  60. * If return true, ReaderActivity.onBackPressed will not run
  61. */
  62. abstract fun onBackPressedWhenNothingCanClose(): Boolean
  63. abstract fun onClickShare()
  64. abstract fun onClickKdanCloud()
  65. abstract fun onClickTextReflow()
  66. abstract fun onClickFileInfo()
  67. abstract fun onClickIBonPrint()
  68. abstract fun onClickPrint()
  69. abstract fun onClickUserGuide()
  70. abstract fun provideReaderSettingDialogFragment(): ReaderSettingDialogFragment
  71. abstract fun providePdfThumbDialogFragment(): PdfThumbDialogFragment
  72. abstract fun isIBonPrintEnabled(): Boolean
  73. /**
  74. * return true = intercept, false = not intercept (same as touch event)
  75. */
  76. open fun onClickCloseButton(): Boolean { return false }
  77. open fun isBottomBarKdanCloudBtnVisible(): Boolean { return true }
  78. open fun insertToRecentDocumentList(filename: String) {}
  79. open fun saveCurrentPageIndex(filename: String, currentPage: Int) {}
  80. open fun loadCurrentPageIndex(filename: String, defaultValue: Int): Int = 0
  81. open fun onOpenedFile() { /* do nothing */ }
  82. open fun onFilePathOrUriError(filePath: String?) { println("onFilePathOrUriError: $filePath") }
  83. // 子類別可複寫此方法以客製化KMPDFPageAdapter
  84. abstract fun providePdfPageAdapter(context: Context, filePickerSupport: FilePicker.FilePickerSupport, kmpdfFactory: KMPDFFactory): KMPDFPageAdapter
  85. open fun provideAdditionalPageManager(): AdditionalPageManager = AdditionalPageManager()
  86. protected open fun onEvent(hookEvent: HookEvent) { /* do nothing */ }
  87. companion object {
  88. const val KEY_FILE_ABSOLUTE = "file_absolutepath"
  89. const val KEY_IS_PASSWORD_ACTIVITY_SHOWING = "isShowPasswordActivity"
  90. const val REQUEST_PASSWORD = 222
  91. }
  92. protected val file: File?
  93. get() {
  94. return viewModel.file
  95. }
  96. protected var filePath: String? = null
  97. private set
  98. protected val pdfInfo: PDFInfo?
  99. get() {
  100. return viewModel.pdfInfo
  101. }
  102. protected val isPasswordProtected: Boolean
  103. get() {
  104. return viewModel.isPasswordProtected
  105. }
  106. protected val currentPageIndex: Int
  107. get() {
  108. return viewModel.pdfInfoHandler.getCurrentPage()
  109. }
  110. protected val pageCount: Int
  111. get() {
  112. return viewModel.pdfInfoHandler.getPdfPageCount()
  113. }
  114. protected val password: String
  115. get() {
  116. return viewModel.password
  117. }
  118. protected val readerModel: ReaderModel
  119. get() {
  120. return viewModel.readerModel
  121. }
  122. protected val additionalPageManager: AdditionalPageManager
  123. get() {
  124. return viewModel.additionalPageManager
  125. }
  126. private val viewModel: ReaderViewModel by viewModel {
  127. val additionalPageManager = provideAdditionalPageManager()
  128. val filePath = intent.getStringExtra(KEY_FILE_ABSOLUTE)
  129. if (!filePath.isNullOrEmpty()) {
  130. insertToRecentDocumentList(filePath as String)
  131. }
  132. val uri = if (!filePath.isNullOrEmpty()) Uri.parse(Uri.encode(filePath)) else null
  133. // The URI may be null in some special case.
  134. // Just use Uri.EMPTY and show an alert dialog in onCreate() when the uri is null.
  135. parametersOf(additionalPageManager, uri ?: Uri.EMPTY)
  136. }
  137. private var isShowPasswordActivity = false
  138. // For hide/show toolbars
  139. private var isHideToolbar = false
  140. private lateinit var originConstraintSet: ConstraintSet
  141. private var menuItemBookmark: MenuItem? = null
  142. private val UI_ANIMATION_DURATION = 300L
  143. private val FRAGMENT_READER_SETTING_TAG = "ReaderSetting"
  144. private val FRAGMENT_PDF_THUMB_TAG = "PdfThumb"
  145. private var w_318: Int = 0
  146. private var w_240: Int = 0
  147. private var w_left: Int = 0
  148. private var oldLeftToolbarType = ReaderViewModel.LeftToolbarType.NONE
  149. private var snackbarMediaBox: Snackbar? = null
  150. private var fileCannotOpenDialog: AlertDialog? = null
  151. private var isHidePdfPage: Boolean by Delegates.observable(false) { property, oldValue, newValue ->
  152. if (newValue == oldValue) return@observable
  153. // println("${property.name} changed from $oldValue to $newValue")
  154. tv_readerActivity_pdfPage.clearAnimation()
  155. when (newValue) {
  156. true -> tv_readerActivity_pdfPage.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade_out))
  157. false -> tv_readerActivity_pdfPage.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade_in))
  158. }
  159. }
  160. override fun onCreate(savedInstanceState: Bundle?) {
  161. super.onCreate(savedInstanceState)
  162. window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
  163. setContentView(R.layout.activity_reader)
  164. setSupportActionBar(toolbar_readerActivity)
  165. isShowPasswordActivity = savedInstanceState?.getBoolean(KEY_IS_PASSWORD_ACTIVITY_SHOWING) ?: false
  166. // Get file path to determine the opening file is pdf or not
  167. filePath = intent.getStringExtra(KEY_FILE_ABSOLUTE)
  168. val displayMetrics = DisplayMetrics()
  169. windowManager.defaultDisplay.getMetrics(displayMetrics)
  170. val width = Math.min(DensityUtil.getScreenWidthPx(baseContext), DensityUtil.getScreenHeightPx(baseContext))
  171. w_318 = 318 * width / 800
  172. w_240 = 240 * width / 800
  173. w_left = resources.getDimension(R.dimen.reader_left_right_toolbar_icon_width).toInt()
  174. cloneOriginConstraintSet()
  175. setupToolbar()
  176. setupRightSideToolbar()
  177. setupMediaBoxView()
  178. viewModel.isOpenedFileLiveData.observe(this, Observer(this::onIsOpenedFileUpdate))
  179. viewModel.fileNameLiveData.observe(this, Observer { supportActionBar?.title = it })
  180. viewModel.annotationModeLiveData.observe(this, Observer(this::onAnnotationModeUpdate))
  181. viewModel.annotationEitModeLiveData.observe(this, Observer(this::onAnnotationEditModeUpdate))
  182. viewModel.isCopyModeLiveData.observe(this, Observer (this::onIsCopyModeUpdate))
  183. viewModel.highLightAttributeLiveData.observe(this, Observer(this::onHighlightAttributeUpdate))
  184. viewModel.strikeAttributeLiveData.observe(this, Observer(this::onStrikeAttributeUpdate))
  185. viewModel.underLineAttributeLiveData.observe(this, Observer(this::onUnderlineAttributeUpdate))
  186. viewModel.inkAttributeLiveData.observe(this, Observer(this::onInkAttributeUpdate))
  187. viewModel.isOpenedFileLiveData.value?.also { isOpened ->
  188. if (isShowPasswordActivity) return@also
  189. if (isOpened) return@also
  190. if (Uri.EMPTY == viewModel.uri) {
  191. showFileCannotOpenDialog(getString(R.string.cannot_open_buffer))
  192. onFilePathOrUriError(filePath)
  193. } else {
  194. val result = viewModel.openPdfFile(
  195. this@ReaderActivity,
  196. password,
  197. Runnable { requestPassword() },
  198. intent.type
  199. )
  200. if (result == ReaderViewModel.OpenFileResult.FAILED) {
  201. showFileCannotOpenDialog(getString(R.string.cannot_open_file_Path, filePath))
  202. }
  203. }
  204. }
  205. setupLeftToolbar()
  206. setupRightToolbar()
  207. setupBottomToolbar()
  208. viewModel.pageIndexLiveData.observe(this, Observer(this::onPageIndexChanged))
  209. viewModel.isPageInBookmarksLiveData.observe(this, Observer(this::onIsPageInBookmarksChanged))
  210. viewModel.isLeftToolbarOpenLiveData.observe(this, Observer(this::onLeftToolbarOpenStateChanged))
  211. viewModel.leftToolbarTypeLiveData.observe(this, Observer(this::onLeftToolbarTypeChanged))
  212. viewModel.eventLiveData.observe(this , Observer(this::onEvent))
  213. isHidePdfPage = true
  214. applySetting()
  215. setupPdfChangedListener()
  216. setupInkMenu()
  217. }
  218. fun setOnClickLinkListener(onClickLinkListener: ReaderViewModel.OnClickLinkListener?) {
  219. viewModel.setOnClickLinkListener(onClickLinkListener)
  220. }
  221. private fun showFileCannotOpenDialog(title: String) {
  222. fileCannotOpenDialog = AlertDialog.Builder(this)
  223. .setTitle(title)
  224. .setCancelable(false)
  225. .setPositiveButton(getString(android.R.string.ok)) { dialog, which ->
  226. finish()
  227. }
  228. .create()
  229. fileCannotOpenDialog?.show()
  230. }
  231. override fun onDestroy() {
  232. fileCannotOpenDialog?.dismiss()
  233. setOnClickLinkListener(null)
  234. super.onDestroy()
  235. }
  236. override fun onSaveInstanceState(outState: Bundle?) {
  237. super.onSaveInstanceState(outState)
  238. outState?.putBoolean(KEY_IS_PASSWORD_ACTIVITY_SHOWING, isShowPasswordActivity)
  239. }
  240. private fun requestPassword(isIncorrectPassword: Boolean = false) {
  241. isShowPasswordActivity = true
  242. val intent = Intent(baseContext, DialogPasswordActivity::class.java)
  243. val bundle = Bundle()
  244. bundle.putBoolean(DialogPasswordActivity.KEY_IS_INCORRECT_PASSWORD, isIncorrectPassword)
  245. intent.putExtras(bundle)
  246. startActivityForResult(intent, REQUEST_PASSWORD)
  247. }
  248. fun isPdf(): Boolean {
  249. var isPdf = filePath?.toLowerCase(Locale.US)?.endsWith(".pdf") ?: false
  250. // TODO this workaround should be removed in the future
  251. // workaround: password-protected pdf is not editable
  252. isPdf = isPdf && !isPasswordProtected
  253. return isPdf
  254. }
  255. final override fun onBackPressed() {
  256. when {
  257. mediaBoxView.isShown -> mediaBoxView.dismiss()
  258. onBackPressedWhenNothingCanClose() -> { /** do nothing **/ }
  259. else -> super.onBackPressed()
  260. }
  261. }
  262. override fun onCreateOptionsMenu(menu: Menu?): Boolean {
  263. menuInflater.inflate(R.menu.reader_more, menu)
  264. menuItemBookmark = menu?.findItem(R.id.item_reader_bookmark)
  265. onIsPageInBookmarksChanged(viewModel.isPageInBookmarksLiveData.value)
  266. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
  267. menu?.findItem(R.id.item_reader_more_print)?.isVisible = false
  268. }
  269. if (!isPdf()) {
  270. menu?.apply {
  271. findItem(R.id.item_reader_more_text_reflow)?.isVisible = false
  272. findItem(R.id.item_reader_more_clear_signature)?.isVisible = false
  273. findItem(R.id.item_reader_more_file_info)?.isVisible = false
  274. findItem(R.id.item_reader_more_print)?.isVisible = false
  275. findItem(R.id.item_ibon_print)?.isVisible = false
  276. }
  277. menuItemBookmark?.isVisible = false
  278. }
  279. if (!isIBonPrintEnabled()) {
  280. menu?.findItem(R.id.item_ibon_print)?.isVisible = false
  281. }
  282. return true
  283. }
  284. @SuppressLint("RestrictedApi")
  285. override fun onPrepareOptionsPanel(view: View?, menu: Menu?): Boolean {
  286. /** Force show action menu item icon **/
  287. menu?.also {
  288. if ("MenuBuilder" != it::javaClass.get().simpleName) return@also
  289. try {
  290. val method = it.javaClass.getDeclaredMethod("setOptionalIconsVisible", java.lang.Boolean.TYPE)
  291. method.isAccessible = true
  292. method.invoke(it, true)
  293. } catch (e: Exception) {
  294. // Do nothing
  295. }
  296. }
  297. return super.onPrepareOptionsPanel(view, menu)
  298. }
  299. protected fun getPageText(pageIndex: Int) = viewModel.getPageText(pageIndex)
  300. private fun cloneOriginConstraintSet() {
  301. if (!isBelowKitkat()) {
  302. originConstraintSet = ConstraintSet().apply {
  303. clone(constrainLayout_readerActivity_root)
  304. }
  305. }
  306. }
  307. private fun onIsCopyModeUpdate(isCopyMode: Boolean?) {
  308. if (isCopyMode == null) return
  309. when (isCopyMode) {
  310. true -> hideTopLeftBottomToolbars()
  311. false -> showAllToolbars()
  312. }
  313. }
  314. private fun onHighlightAttributeUpdate(annotationAttribute: AnnotationAttribute?) {
  315. if (annotationAttribute == null) return
  316. view_readerActivity_highLightStroke.setBackgroundColor(annotationAttribute.color)
  317. }
  318. private fun onStrikeAttributeUpdate(annotationAttribute: AnnotationAttribute?) {
  319. if (annotationAttribute == null) return
  320. view_readerActivity_strikeStroke.setBackgroundColor(annotationAttribute.color)
  321. }
  322. private fun onUnderlineAttributeUpdate(annotationAttribute: AnnotationAttribute?) {
  323. if (annotationAttribute == null) return
  324. view_readerActivity_underlineStroke.setBackgroundColor(annotationAttribute.color)
  325. }
  326. private fun onInkAttributeUpdate(inkAttribute: InkAttribute?) {
  327. if (inkAttribute == null) return
  328. val color = inkAttribute.color
  329. AnnotationColor.values().forEach {
  330. if (ContextCompat.getColor(this, it.colorResId) == color) {
  331. val drawableResId = when (it) {
  332. AnnotationColor.Color1 -> R.drawable.ic_reader_right_toolbar_ink_color1
  333. AnnotationColor.Color2 -> R.drawable.ic_reader_right_toolbar_ink_color2
  334. AnnotationColor.Color3 -> R.drawable.ic_reader_right_toolbar_ink_color3
  335. AnnotationColor.Color4 -> R.drawable.ic_reader_right_toolbar_ink_color4
  336. AnnotationColor.Color5 -> R.drawable.ic_reader_right_toolbar_ink_color5
  337. AnnotationColor.Color6 -> R.drawable.ic_reader_right_toolbar_ink_color6
  338. AnnotationColor.Color7 -> R.drawable.ic_reader_right_toolbar_ink_color7
  339. AnnotationColor.Color8 -> R.drawable.ic_reader_right_toolbar_ink_color8
  340. AnnotationColor.Color9 -> R.drawable.ic_reader_right_toolbar_ink_color9
  341. AnnotationColor.Color10 -> R.drawable.ic_reader_right_toolbar_ink_color10
  342. }
  343. iv_readerActivity_ink.setImageResource(drawableResId)
  344. return
  345. }
  346. }
  347. }
  348. private fun onAnnotationModeUpdate(annotationMode: ReaderViewModel.AnnotationMode?) {
  349. if (annotationMode == null) return
  350. if (viewModel.isCrop) return
  351. val map = HashMap<ReaderViewModel.AnnotationMode, View>().apply {
  352. put(ReaderViewModel.AnnotationMode.HIGHLIGHT, iv_readerActivity_highLight)
  353. put(ReaderViewModel.AnnotationMode.STRIKE, iv_readerActivity_strike)
  354. put(ReaderViewModel.AnnotationMode.UNDERLINE, iv_readerActivity_underline)
  355. put(ReaderViewModel.AnnotationMode.INK, iv_readerActivity_ink)
  356. }
  357. map.forEach {
  358. if (it.key == annotationMode) {
  359. val color = ContextCompat.getColor(this, R.color.reader_right_toolbar_selected_bg)
  360. it.value.setBackgroundColor(color)
  361. } else {
  362. it.value.setBackgroundDrawable(null)
  363. }
  364. }
  365. when (annotationMode == ReaderViewModel.AnnotationMode.NONE) {
  366. true -> showAllToolbars()
  367. false -> hideTopLeftBottomToolbars()
  368. }
  369. readerModel.onPdfChangedListener?.onPageUpdated(arrayListOf(currentPageIndex))
  370. if (viewModel.annotationModeLiveData.value != ReaderViewModel.AnnotationMode.INK) {
  371. AnimationUtil.hideViewAlpha(view_ink_menu)
  372. }
  373. }
  374. private fun setupInkMenu() {
  375. val btnInkUndo = findViewById<ImageButton>(R.id.btn_ink_undo)
  376. val btnInkRedo = findViewById<ImageButton>(R.id.btn_ink_redo)
  377. val btnInkClean = findViewById<ImageButton>(R.id.btn_ink_clean)
  378. val btnInkDone = findViewById<ImageButton>(R.id.btn_ink_done)
  379. btnInkUndo.setOnClickListener {
  380. viewModel.undoInk()
  381. }
  382. btnInkRedo.setOnClickListener {
  383. viewModel.redoInk()
  384. }
  385. btnInkClean.setOnClickListener {
  386. viewModel.cleanInk()
  387. }
  388. btnInkDone.setOnClickListener {
  389. viewModel.finishInk()
  390. }
  391. }
  392. private fun onAnnotationEditModeUpdate(mode: ReaderViewModel.AnnotationEitMode?) {
  393. when (mode) {
  394. ReaderViewModel.AnnotationEitMode.NULL -> {
  395. showAllToolbars()
  396. isHideToolbar = false
  397. snackbarMediaBox?.dismiss()
  398. snackbarMediaBox = null
  399. }
  400. ReaderViewModel.AnnotationEitMode.FORMTEXT_MODIFY, ReaderViewModel.AnnotationEitMode.SIGN_MODIFY -> {
  401. hideAllToolbars()
  402. isHideToolbar = true
  403. }
  404. ReaderViewModel.AnnotationEitMode.FREETEXT_MODIFY, ReaderViewModel.AnnotationEitMode.STAMP_MODIFY, ReaderViewModel.AnnotationEitMode.SHAPE_MODIFY -> {
  405. hideAllToolbars()
  406. isHideToolbar = true
  407. snackbarMediaBox?.dismiss()
  408. snackbarMediaBox = null
  409. }
  410. else -> {}
  411. }
  412. isHidePdfPage = true
  413. readerModel.onPdfChangedListener?.onPageUpdated(arrayListOf(currentPageIndex))
  414. }
  415. private fun onIsOpenedFileUpdate(isOpened: Boolean?) {
  416. if (isOpened == null) return
  417. val container = viewGroup_readerActivity_container
  418. container.removeAllViews()
  419. if (!isOpened) return
  420. val context = this
  421. // 採用客製化的KMPDFReaderView以覆寫畫面點擊判斷和當前頁面計算方法
  422. val readerView = object : AdditionalPageReaderView(context, additionalPageManager.pageConverter) {
  423. @SuppressLint("ClickableViewAccessibility")
  424. override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
  425. if (motionEvent.action == MotionEvent.ACTION_UP) {
  426. if (viewModel.isCopyModeLiveData.value == true) {
  427. if (viewModel.copySelection()) {
  428. Toast.makeText(context, R.string.reader_copy_text_success, Toast.LENGTH_SHORT).show()
  429. }
  430. }
  431. }
  432. return super.onTouchEvent(motionEvent)
  433. }
  434. override fun onMoveToChild(pageIndex: Int) {
  435. viewModel.setPageIndex(pageIndex)
  436. // 畫面更新時應通知additionalPageManager
  437. additionalPageManager.onPageChanged(pageIndex)
  438. }
  439. override fun onScrolling() {
  440. val isEditMode = viewModel.annotationModeLiveData.value != ReaderViewModel.AnnotationMode.NONE
  441. val isCopyMode = viewModel.isCopyModeLiveData.value != false
  442. if (!isEditMode && !isCopyMode && !isHideToolbar) {
  443. hideAllToolbars()
  444. isHideToolbar = true
  445. }
  446. isHidePdfPage = isEditMode || isCopyMode
  447. }
  448. override fun onTapMainDocArea() {
  449. if (viewModel.annotationEitModeLiveData.value == ReaderViewModel.AnnotationEitMode.FORMTEXT_MODIFY) {
  450. return
  451. }
  452. when (isHideToolbar) {
  453. true -> {
  454. showAllToolbars()
  455. viewModel.clearSelection()
  456. }
  457. false -> hideAllToolbars()
  458. }
  459. isHideToolbar = !isHideToolbar
  460. }
  461. }
  462. viewModel.setReaderView(readerView)
  463. /**
  464. * 使用客製化KMPDFPageAdapter取代內建的Adapter
  465. * 注意,需要在KMPDFFactory::setReaderView之後調用才不會被複寫
  466. */
  467. viewModel.getReaderView()?.apply {
  468. adapter = providePdfPageAdapter(
  469. context,
  470. FilePicker.FilePickerSupport {},
  471. viewModel.readerModel.kmpdfFactory!!
  472. )
  473. refresh(true)
  474. }
  475. if (!filePath.isNullOrEmpty()) {
  476. val defaultValue = viewModel.pageIndexLiveData.value ?: 0
  477. val pageIndex = loadCurrentPageIndex(filePath as String, defaultValue)
  478. viewModel.pdfInfoHandler.goToCurrentPage(pageIndex)
  479. viewModel.fileNameLiveData.postValue(File(filePath as String).name)
  480. }
  481. initTextBoxContextMenuActions()
  482. container.addView(readerView)
  483. val model = SharePrefsModel(this)
  484. onReadModeChanged(model.readMode.value ?: 0)
  485. if (model.isCropModeEnable.value == true) {
  486. readerView.post {
  487. onCropModeEnableChanged(true)
  488. }
  489. }
  490. onOpenedFile()
  491. }
  492. fun removeAdditionalPages() {
  493. if (additionalPageManager.displayStrategyType == AdditionalPageDisplayStrategyType.HIDE) return
  494. val handler = viewModel.pdfInfoHandler
  495. val pageIndex = additionalPageManager.pageConverter.convertToRawPageIndex(currentPageIndex, true)
  496. .coerceAtMost(pageCount - 1)
  497. additionalPageManager.displayStrategyType = AdditionalPageDisplayStrategyType.HIDE
  498. viewModel.getReaderView()?.refresh(true)
  499. handler.goToCurrentPage(pageIndex)
  500. }
  501. private fun initTextBoxContextMenuActions() {
  502. viewModel.setTextBoxContextMenuActions(object : TextBoxContextMenuActionListener {
  503. override fun onDelete(): Boolean {
  504. viewModel.deleteSelectedTextBox()
  505. return true
  506. }
  507. override fun onEditStyle(): Boolean {
  508. mediaBoxView.showTextBoxStyleView()
  509. viewModel.editSelectedTextBoxStyle()
  510. return true
  511. }
  512. override fun onEditText(): Boolean {
  513. hideAllToolbars()
  514. isHideToolbar = true
  515. viewModel.editSelectedTextBoxText()
  516. return true
  517. }
  518. override fun onCopy(): Boolean {
  519. viewModel.copySelectedTextBox()
  520. return true
  521. }
  522. })
  523. }
  524. override fun onStop() {
  525. // Cancel form editing state to prevent soft keyboard bouncing
  526. if (viewModel.annotationEitModeLiveData.value == ReaderViewModel.AnnotationEitMode.FORMTEXT_MODIFY) {
  527. // ugly method to call package function readerView?.displayedView?.releaseFormText()
  528. val event = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, -1000f, -1000f, 0)
  529. viewModel.getReaderView()?.onSingleTapUp(event)
  530. }
  531. if (viewModel.annotationModeLiveData.value == ReaderViewModel.AnnotationMode.INK) {
  532. viewModel.stopAnnotationMode()
  533. viewModel.annotationModeLiveData.value = ReaderViewModel.AnnotationMode.NONE
  534. }
  535. viewModel.saveModifyingAnnotation()
  536. viewModel.temporarySave()
  537. if (!filePath.isNullOrEmpty()) {
  538. saveCurrentPageIndex(filePath as String, currentPageIndex)
  539. }
  540. super.onStop()
  541. }
  542. override fun onResume() {
  543. super.onResume()
  544. viewModel.restoreAnnotationEditMode()
  545. }
  546. private fun onPageIndexChanged(pageIndex: Int?) {
  547. pageIndex?.also {
  548. viewModel.setBookmarkDisplay(pageIndex)
  549. readerModel.onPdfChangedListener?.onPageSelected(pageIndex)
  550. tv_readerActivity_pdfPage.post {
  551. tv_readerActivity_pdfPage.text="${(pageIndex) + 1}/${viewModel.pdfInfoHandler.getPdfPageCount()}"
  552. }
  553. }
  554. }
  555. private fun onIsPageInBookmarksChanged(isPageInBookmarks: Boolean?) {
  556. isPageInBookmarks?.let {
  557. if (it) {
  558. menuItemBookmark?.icon = ContextCompat.getDrawable(this, R.drawable.ic_bookmark_fill)
  559. } else {
  560. menuItemBookmark?.icon = ContextCompat.getDrawable(this, R.drawable.ic_bookmark_border)
  561. }
  562. }
  563. }
  564. private fun isBelowKitkat(): Boolean {
  565. return true
  566. // return Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT
  567. }
  568. private fun showAllToolbars() {
  569. if (isBelowKitkat()) {
  570. val duration = ((1 - toolbar_readerActivity.alpha) * UI_ANIMATION_DURATION).toLong()
  571. AnimationUtil.showViewFromTopToBottom(toolbar_readerActivity, duration)
  572. AnimationUtil.showViewFromBottomToTop(viewGroup_readerActivity_bottomToolbar, duration)
  573. AnimationUtil.showViewFromLeftToRight(viewGroup_readerActivity_leftToolbar, duration)
  574. AnimationUtil.showViewFromRightToLeft(viewGroup_readerActivity_rightToolbar, duration)
  575. } else {
  576. constrainLayout_readerActivity_root.applyConstraintSet(originConstraintSet, UI_ANIMATION_DURATION)
  577. }
  578. isHidePdfPage = true
  579. }
  580. private fun hideAllToolbars() {
  581. if (isBelowKitkat()) {
  582. val duration = (toolbar_readerActivity.alpha * UI_ANIMATION_DURATION).toLong()
  583. AnimationUtil.hideViewFromBottomToTop(toolbar_readerActivity, duration)
  584. AnimationUtil.hideViewFromTopToBottom(viewGroup_readerActivity_bottomToolbar, duration)
  585. AnimationUtil.hideViewFromRightToLeft(viewGroup_readerActivity_leftToolbar, duration)
  586. AnimationUtil.hideViewFromLeftToRight(viewGroup_readerActivity_rightToolbar, duration)
  587. hideSearchViewSoftKeyboard()
  588. } else {
  589. constrainLayout_readerActivity_root.applyConstraintSet(this, R.layout.activity_reader_hide_all, UI_ANIMATION_DURATION)
  590. }
  591. }
  592. private fun hideTopLeftBottomToolbars() {
  593. if (isBelowKitkat()) {
  594. val duration = (toolbar_readerActivity.alpha * UI_ANIMATION_DURATION).toLong()
  595. AnimationUtil.hideViewFromBottomToTop(toolbar_readerActivity, duration)
  596. AnimationUtil.hideViewFromTopToBottom(viewGroup_readerActivity_bottomToolbar, duration)
  597. AnimationUtil.hideViewFromRightToLeft(viewGroup_readerActivity_leftToolbar, duration)
  598. hideSearchViewSoftKeyboard()
  599. } else {
  600. constrainLayout_readerActivity_root.applyConstraintSet(this, R.layout.activity_reader_hide_top_left_bottom, UI_ANIMATION_DURATION)
  601. }
  602. }
  603. private fun setupPdfChangedListener() {
  604. readerModel.onPdfChangedListener = object : OnPdfChangedListener {
  605. override fun onPageCreated(pages: List<Int>) {
  606. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
  607. }
  608. override fun onPageDeleted(pages: List<Int>) {
  609. val view = linearLayout_readerActivity_leftToolbarContainer.getChildAt(0)
  610. if (view is LeftToolbarView) {
  611. view.updateContent()
  612. view.onScrollToContent(currentPageIndex, true)
  613. }
  614. }
  615. override fun onPageSelected(pageIndex: Int) {
  616. val view = linearLayout_readerActivity_leftToolbarContainer.getChildAt(0)
  617. if (view is LeftToolbarView) {
  618. view.onScrollToContent(currentPageIndex, true)
  619. }
  620. }
  621. override fun onPageUpdated(pages: List<Int>) {
  622. val view = linearLayout_readerActivity_leftToolbarContainer.getChildAt(0)
  623. if (view is LeftToolbarView) {
  624. view.updateContent()
  625. view.onScrollToContent(currentPageIndex, true)
  626. }
  627. }
  628. }
  629. }
  630. private fun updateBookmarkView() {
  631. val view = linearLayout_readerActivity_leftToolbarContainer.getChildAt(0)
  632. if (view is BookmarkView) {
  633. view.updateContent()
  634. view.onScrollToContent(currentPageIndex, true)
  635. }
  636. }
  637. private fun setupToolbar() {
  638. toolbar_readerActivity.setNavigationOnClickListener {
  639. if (!onClickCloseButton()) {
  640. finish()
  641. }
  642. }
  643. toolbar_readerActivity.setOnMenuItemClickListener {
  644. when (it.itemId) {
  645. R.id.item_reader_bookmark -> onClickBookmark()
  646. R.id.item_reader_more_text_reflow -> onClickTextReflow()
  647. R.id.item_reader_more_file_info -> onClickFileInfo()
  648. R.id.item_ibon_print -> onClickIBonPrint()
  649. R.id.item_reader_more_print -> onClickPrint()
  650. R.id.item_reader_more_user_guide -> onClickUserGuide()
  651. R.id.item_reader_more_clear_signature -> onClickClearSignature()
  652. }
  653. return@setOnMenuItemClickListener true
  654. }
  655. }
  656. private fun onClickBookmark() {
  657. onEvent(HookEvent.OnClickTopBookmarkBtn())
  658. viewModel.isPageInBookmarksLiveData.value?.let {
  659. if (!it) {
  660. showAddBookmarkDialog()
  661. } else {
  662. showDeleteBookmarkDialog()
  663. }
  664. }
  665. }
  666. private fun onClickClearSignature() {
  667. AlertDialog.Builder(this)
  668. .setMessage(R.string.reader_clear_signature_dialog_msg)
  669. .setPositiveButton(android.R.string.yes) { _, _ -> viewModel.clearSignature() }
  670. .setNegativeButton(android.R.string.no) { _, _ -> /* do nothing */ }
  671. .show()
  672. }
  673. private fun showAddBookmarkDialog() {
  674. val alertDialogBuilder = AlertDialog.Builder(this)
  675. val editText = EditText(this)
  676. val layoutParams =
  677. LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)
  678. editText.layoutParams = layoutParams
  679. val defaultTitle = getString(R.string.dialogAddBookmark_page) + " " + (viewModel.pdfInfoHandler.getCurrentPage() + 1)
  680. editText.setText(defaultTitle, TextView.BufferType.EDITABLE)
  681. editText.setSelection(0, defaultTitle.length)
  682. alertDialogBuilder.setTitle(getString(R.string.reader_alert_dialog_title_add_bookmark))
  683. alertDialogBuilder.setView(editText)
  684. alertDialogBuilder.setPositiveButton(getString(R.string.reader_alert_dialog_button_confirm)) { dialog, which ->
  685. viewModel.addBookmark(editText.text.toString())
  686. updateBookmarkView()
  687. }
  688. alertDialogBuilder.setNegativeButton(getString(R.string.reader_alert_dialog_button_cancel)) { dialog, which ->
  689. }
  690. alertDialogBuilder.create().show()
  691. editText.requestFocus()
  692. }
  693. private fun getBookmarkTitle(page: Int): String {
  694. val bookmarks = viewModel.bookmarkHandler.getBookmarks()
  695. for (bookmark in bookmarks) {
  696. if (bookmark.pageNum == page) {
  697. return bookmark.title
  698. }
  699. }
  700. return ""
  701. }
  702. private fun showDeleteBookmarkDialog() {
  703. val alertDialogBuilder = AlertDialog.Builder(this)
  704. alertDialogBuilder.setTitle(getString(R.string.reader_alert_dialog_title_delete_bookmark))
  705. val bookmarkTitle = getBookmarkTitle(viewModel.pdfInfoHandler.getCurrentPage())
  706. val message = String.format(getString(R.string.reader_alert_dialog_message_delete_bookmark), bookmarkTitle)
  707. alertDialogBuilder.setMessage(message)
  708. alertDialogBuilder.setPositiveButton(getString(R.string.reader_alert_dialog_button_confirm)) { dialog, which ->
  709. viewModel.deleteBookmark()
  710. updateBookmarkView()
  711. }
  712. alertDialogBuilder.setNegativeButton(getString(R.string.reader_alert_dialog_button_cancel)) { dialog, which ->
  713. }
  714. alertDialogBuilder.create().show()
  715. }
  716. private fun hideSearchViewSoftKeyboard() {
  717. val view = linearLayout_readerActivity_leftToolbarContainer.getChildAt(0)
  718. if (view is SearchView) {
  719. view.hideSoftKeyboard()
  720. }
  721. }
  722. private fun setupLeftToolbar() {
  723. viewGroup_readerActivity_leftToolbar.setScroll(false)
  724. setLeftToolbarWidth(w_240)
  725. viewGroup_readerActivity_leftToolbar.smoothScrollTo(w_240, 0, 0)
  726. val child = layoutInflater.inflate(R.layout.view_thumbnail, null)
  727. linearLayout_readerActivity_leftToolbarContainer.addView(child)
  728. fun onClickLeftToolbarItem(leftToolbarType: ReaderViewModel.LeftToolbarType) {
  729. viewModel.setLeftToolbarType(leftToolbarType)
  730. updateLeftToolbarOpenState(leftToolbarType)
  731. }
  732. iv_readerActivity_thumbnail.setOnClickListener { onClickLeftToolbarItem(ReaderViewModel.LeftToolbarType.THUMBNAIL) }
  733. iv_readerActivity_bookmark.setOnClickListener { onClickLeftToolbarItem(ReaderViewModel.LeftToolbarType.BOOKMARK) }
  734. iv_readerActivity_outline.setOnClickListener { onClickLeftToolbarItem(ReaderViewModel.LeftToolbarType.OUTLINE) }
  735. iv_readerActivity_search.setOnClickListener { onClickLeftToolbarItem(ReaderViewModel.LeftToolbarType.SEARCH) }
  736. if (!isPdf()) {
  737. iv_readerActivity_bookmark.visibility = View.GONE
  738. if (!isPasswordProtected) iv_readerActivity_outline.visibility = View.GONE
  739. }
  740. }
  741. override fun onReadModeChanged(readMode: Int) {
  742. viewModel.readMode = when (readMode) {
  743. ReadingModeSelectView.modeDay -> ReaderViewModel.ReadMode.READ_MODE_DAY
  744. ReadingModeSelectView.modeNight -> ReaderViewModel.ReadMode.READ_MODE_NIGHT
  745. ReadingModeSelectView.modeSoft -> ReaderViewModel.ReadMode.READ_MODE_SOFT
  746. ReadingModeSelectView.modeChromeWhite -> ReaderViewModel.ReadMode.MODE_CHROME_WHITE
  747. ReadingModeSelectView.modeEtonBlue -> ReaderViewModel.ReadMode.MODE_ETON_BLUE
  748. ReadingModeSelectView.modeWhiteLilac -> ReaderViewModel.ReadMode.MODE_WHITE_LILAC
  749. else -> ReaderViewModel.ReadMode.READ_MODE_DAY
  750. }
  751. }
  752. override fun onCropModeEnableChanged(enable: Boolean) {
  753. viewModel.isCrop = enable
  754. val buttons = arrayOf(iv_readerActivity_highLight, iv_readerActivity_strike, iv_readerActivity_underline, iv_readerActivity_ink, ib_readerActivity_bottomToolbarMediaBox)
  755. if (enable) {
  756. viewModel.stopAnnotationMode()
  757. val color = ContextCompat.getColor(baseContext, R.color.reader_right_toolbar_disabled_bg)
  758. for (button in buttons) {
  759. button.isEnabled = false
  760. button.setBackgroundColor(color)
  761. }
  762. } else {
  763. for (button in buttons) {
  764. button.isEnabled = true
  765. button.setBackgroundDrawable(null)
  766. }
  767. }
  768. }
  769. override fun onShowStateBarEnableChanged(enable: Boolean, dialog: Dialog?) {
  770. when (enable) {
  771. true -> {
  772. dialog?.window?.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
  773. window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
  774. }
  775. false -> {
  776. dialog?.window?.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
  777. window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
  778. }
  779. }
  780. }
  781. override fun onTurnPageDirectionChanged(direction: Int) {
  782. viewModel.viewDirection = when (direction == 0) {
  783. true -> ReaderViewModel.ViewDirection.VERTICAL_SINGLE_PAGE_CONTINUES
  784. false -> ReaderViewModel.ViewDirection.HORIZONTAL_SINGLE_PAGE
  785. }
  786. }
  787. override fun onShowTurnPageBtnEnableChanged(enable: Boolean) {
  788. when (enable) {
  789. true -> {
  790. ib_readerActivity_bottomToolbarPrevious.visibility = View.VISIBLE
  791. ib_readerActivity_bottomToolbarNext.visibility = View.VISIBLE
  792. }
  793. false -> {
  794. ib_readerActivity_bottomToolbarPrevious.visibility = View.GONE
  795. ib_readerActivity_bottomToolbarNext.visibility = View.GONE
  796. }
  797. }
  798. }
  799. override fun onLockScreenSleepEnableChanged(enable: Boolean) {
  800. when (enable) {
  801. true -> window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
  802. false -> window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
  803. }
  804. }
  805. override fun onShowCommentToolEnableChanged(enable: Boolean) {
  806. }
  807. override fun onShowBOTAToolEnableChanged(enable: Boolean) {
  808. }
  809. private fun setupRightSideToolbar() {
  810. iv_readerActivity_setting.setOnClickListener {
  811. val settingDialog = provideReaderSettingDialogFragment()
  812. settingDialog.show(supportFragmentManager, FRAGMENT_READER_SETTING_TAG)
  813. }
  814. }
  815. private fun applySetting() {
  816. val model = SharePrefsModel(this)
  817. onShowStateBarEnableChanged(model.isShowStateBarEnable.value ?: false, null)
  818. onTurnPageDirectionChanged(model.turnPageDirection.value ?: 0)
  819. onShowTurnPageBtnEnableChanged(model.isShowTurnPageBtnEnable.value ?: false)
  820. onLockScreenSleepEnableChanged(model.isLockScreenSleepEnable.value ?: false)
  821. onShowCommentToolEnableChanged(model.isShowCommentToolEnable.value ?: false)
  822. onShowBOTAToolEnableChanged(model.isShowBOTAToolEnable.value ?: false)
  823. }
  824. override fun isShowStateBar(): Boolean {
  825. return SharePrefsModel(this).isShowStateBarEnable.value ?: false
  826. }
  827. private fun setupBottomToolbar() {
  828. ib_readerActivity_bottomToolbarMediaBox.setOnClickListener {
  829. onEvent(HookEvent.OnClickBottomMediaBoxBtn())
  830. mediaBoxView.show()
  831. }
  832. ib_readerActivity_bottomToolbarViewAll.setOnClickListener {
  833. onEvent(HookEvent.OnClickBottomThumbnailBtn())
  834. onClickBottomThumb1()
  835. }
  836. ib_readerActivity_bottomToolbarPrevious.setOnClickListener {
  837. onEvent(HookEvent.OnClickBottomPreviousBtn())
  838. val handler = viewModel.pdfInfoHandler
  839. val pageIndex = handler.getCurrentPage()
  840. val previousPageIndex = Math.max(pageIndex - 1, 0)
  841. handler.goToCurrentPage(previousPageIndex)
  842. }
  843. ib_readerActivity_bottomToolbarNext.setOnClickListener {
  844. onEvent(HookEvent.OnClickBottomNextBtn())
  845. val handler = viewModel.pdfInfoHandler
  846. val pageIndex = handler.getCurrentPage()
  847. val nextPageIndex = Math.min(pageIndex + 1, handler.getPdfPageCount() - 1)
  848. handler.goToCurrentPage(nextPageIndex)
  849. }
  850. ib_readerActivity_bottomToolbarKdanCloud.visibility = if (isBottomBarKdanCloudBtnVisible()) {
  851. View.VISIBLE
  852. } else {
  853. View.GONE
  854. }
  855. ib_readerActivity_bottomToolbarKdanCloud.setOnClickListener {
  856. onClickKdanCloud()
  857. }
  858. ib_readerActivity_bottomToolbarShare.setOnClickListener {
  859. onClickShare()
  860. }
  861. if (!isPdf()) {
  862. ib_readerActivity_bottomToolbarMediaBox.visibility = View.GONE
  863. }
  864. }
  865. private fun setupMediaBoxView() {
  866. fun showSnackbar(title: String, btnText: String) {
  867. snackbarMediaBox = Snackbar.make(mediaBoxView, title, Snackbar.LENGTH_INDEFINITE)
  868. .setAction(btnText) {
  869. viewModel.clearSelection()
  870. mediaBoxView.post {
  871. mediaBoxView.show()
  872. snackbarMediaBox?.dismiss()
  873. snackbarMediaBox = null
  874. }
  875. }
  876. snackbarMediaBox?.setActionTextColor(ContextCompat.getColor(this, R.color.bright_blue))
  877. snackbarMediaBox?.show()
  878. }
  879. mediaBoxView.apply {
  880. onShowListener = Runnable {
  881. onMediaBoxShown()
  882. snackbarMediaBox?.dismiss()
  883. snackbarMediaBox = null
  884. }
  885. onDismissListener = Runnable {
  886. onMediaBoxDismissed()
  887. snackbarMediaBox?.dismiss()
  888. snackbarMediaBox = null
  889. }
  890. onClickAddButtonListener = object : MediaBoxView.OnClickHeaderButtonListener {
  891. override fun onClickTextBoxAddButton(textBoxTabView: TextBoxTabView) {
  892. dismiss(false)
  893. viewModel.setTextBoxAttribute(textBoxTabView.getTextBoxAttribute())
  894. showSnackbar(resources.getString(R.string.reader_mediaBox_snakebar_textBox), resources.getString(R.string.reader_mediaBox_snakebar_undo))
  895. }
  896. override fun onClickTextBoxStyleApplyButton(textBoxTabView: TextBoxTabView) {
  897. dismiss(false)
  898. viewModel.setTextBoxAttribute(textBoxTabView.getTextBoxAttribute(), true)
  899. }
  900. override fun onClickSignatureAddButton(signatureTabView: SignatureTabView) {
  901. dismiss(false)
  902. viewModel.setSignatureAttribute(signatureTabView.getSignatureAttribute())
  903. showSnackbar(resources.getString(R.string.reader_mediaBox_snakebar_signature), resources.getString(R.string.reader_mediaBox_snakebar_undo))
  904. }
  905. override fun onClickStampAddButton(stampTabView: StampTabView) {
  906. dismiss(false)
  907. viewModel.setStampAttribute(stampTabView.getStampAttribute())
  908. showSnackbar(resources.getString(R.string.reader_mediaBox_snakebar_stamp), resources.getString(R.string.reader_mediaBox_snakebar_undo))
  909. }
  910. override fun onClickShapeAddButton(shapeTabView: ShapeTabView) {
  911. dismiss(false)
  912. viewModel.setShapeAttribute(shapeTabView.getShapeAttribute())
  913. showSnackbar(resources.getString(R.string.reader_mediaBox_snakebar_shape), resources.getString(R.string.reader_mediaBox_snakebar_undo))
  914. }
  915. }
  916. }
  917. }
  918. private fun setupRightToolbar() {
  919. viewGroup_readerActivity_rightToolbar.setScroll(false)
  920. iv_readerActivity_highLight.apply {
  921. setOnClickListener {
  922. onEvent(HookEvent.OnClickHighlightBtn())
  923. viewModel.onClickHighlightBtn()
  924. }
  925. setOnLongClickListener { btn ->
  926. onEvent(HookEvent.OnLongClickHighlightBtn())
  927. viewModel.onLongClickHighlightBtn()
  928. val context = this@ReaderActivity
  929. AnnotationAttributeWindow(context, true).also { window ->
  930. viewModel.highLightAttributeLiveData.value?.let { attr ->
  931. window.annotationAttributeView.annotationAttribute = attr
  932. }
  933. window.annotationAttributeView.onChangeListener = object : AnnotationAttributeView.OnChangeListener {
  934. override fun onChange(annotationAttributeView: AnnotationAttributeView) {
  935. onEvent(HookEvent.OnUserChangeHighlightPropertySuccess())
  936. viewModel.setHighLightAttributes(annotationAttributeView.annotationAttribute)
  937. }
  938. }
  939. val contentView = window.contentView.apply {
  940. val w = Utils.makeDropDownMeasureSpec(window.width)
  941. val y = Utils.makeDropDownMeasureSpec(window.height)
  942. measure(w, y)
  943. }
  944. val space = resources.getDimension(R.dimen.reader_annotation_property_setting_window_right_toolbar_space)
  945. val xOff = (-contentView.measuredWidth - space).toInt()
  946. val yOff = -btn.height
  947. window.showAsDropDown(btn, xOff, yOff)
  948. }
  949. return@setOnLongClickListener true
  950. }
  951. }
  952. iv_readerActivity_strike.apply {
  953. setOnClickListener {
  954. onEvent(HookEvent.OnClickStrikeoutBtn())
  955. viewModel.onClickStrikeBtn()
  956. }
  957. setOnLongClickListener { btn ->
  958. onEvent(HookEvent.OnLongClickStrikeoutBtn())
  959. viewModel.onLongClickStrikeBtn()
  960. val context = this@ReaderActivity
  961. AnnotationAttributeWindow(context, true).also { window ->
  962. viewModel.strikeAttributeLiveData.value?.let { attr ->
  963. window.annotationAttributeView.annotationAttribute = attr
  964. }
  965. window.annotationAttributeView.onChangeListener = object : AnnotationAttributeView.OnChangeListener {
  966. override fun onChange(annotationAttributeView: AnnotationAttributeView) {
  967. onEvent(HookEvent.OnUserChangeStrikeoutPropertySuccess())
  968. viewModel.setStrikeOutAttributes(annotationAttributeView.annotationAttribute)
  969. }
  970. }
  971. val contentView = window.contentView.apply {
  972. val w = Utils.makeDropDownMeasureSpec(window.width)
  973. val y = Utils.makeDropDownMeasureSpec(window.height)
  974. measure(w, y)
  975. }
  976. val space = resources.getDimension(R.dimen.reader_annotation_property_setting_window_right_toolbar_space)
  977. val xOff = (-contentView.measuredWidth - space).toInt()
  978. val yOff = -btn.height
  979. window.showAsDropDown(btn, xOff, yOff)
  980. }
  981. return@setOnLongClickListener true
  982. }
  983. }
  984. iv_readerActivity_underline.apply {
  985. setOnClickListener {
  986. onEvent(HookEvent.OnClickUnderlineBtn())
  987. viewModel.onClickUnderlineBtn()
  988. }
  989. setOnLongClickListener { btn ->
  990. onEvent(HookEvent.OnLongClickUnderlineBtn())
  991. viewModel.onLongClickUnderlineBtn()
  992. val context = this@ReaderActivity
  993. AnnotationAttributeWindow(context, true).also { window ->
  994. viewModel.underLineAttributeLiveData.value?.let { attr ->
  995. window.annotationAttributeView.annotationAttribute = attr
  996. }
  997. window.annotationAttributeView.onChangeListener = object : AnnotationAttributeView.OnChangeListener {
  998. override fun onChange(annotationAttributeView: AnnotationAttributeView) {
  999. onEvent(HookEvent.OnUserChangeUnderlinePropertySuccess())
  1000. viewModel.setUnderLineAttributes(annotationAttributeView.annotationAttribute)
  1001. }
  1002. }
  1003. val contentView = window.contentView.apply {
  1004. val w = Utils.makeDropDownMeasureSpec(window.width)
  1005. val y = Utils.makeDropDownMeasureSpec(window.height)
  1006. measure(w, y)
  1007. }
  1008. val space = resources.getDimension(R.dimen.reader_annotation_property_setting_window_right_toolbar_space)
  1009. val xOff = (-contentView.measuredWidth - space).toInt()
  1010. val yOff = -btn.height
  1011. window.showAsDropDown(btn, xOff, yOff)
  1012. }
  1013. return@setOnLongClickListener true
  1014. }
  1015. }
  1016. iv_readerActivity_ink.apply {
  1017. setOnClickListener {
  1018. onEvent(HookEvent.OnClickInkBtn())
  1019. if (viewModel.annotationModeLiveData.value == ReaderViewModel.AnnotationMode.INK) {
  1020. AnimationUtil.hideViewAlpha(view_ink_menu)
  1021. } else {
  1022. AnimationUtil.showViewAlpha(view_ink_menu)
  1023. }
  1024. viewModel.onClickInkBtn()
  1025. }
  1026. setOnLongClickListener { btn ->
  1027. onEvent(HookEvent.OnLongClickInkBtn())
  1028. viewModel.onLongClickInkBtn()
  1029. val context = this@ReaderActivity
  1030. AnnotationAttributeWindow(context, false).also { window ->
  1031. viewModel.inkAttributeLiveData.value?.let { attr ->
  1032. window.annotationAttributeView.inkAttribute = attr
  1033. }
  1034. window.annotationAttributeView.onChangeListener = object : AnnotationAttributeView.OnChangeListener {
  1035. override fun onChange(annotationAttributeView: AnnotationAttributeView) {
  1036. onEvent(HookEvent.OnUserChangeInkPropertySuccess())
  1037. viewModel.setInkAttributes(annotationAttributeView.inkAttribute)
  1038. }
  1039. }
  1040. val contentView = window.contentView.apply {
  1041. val w = Utils.makeDropDownMeasureSpec(window.width)
  1042. val y = Utils.makeDropDownMeasureSpec(window.height)
  1043. measure(w, y)
  1044. }
  1045. val space = resources.getDimension(R.dimen.reader_annotation_property_setting_window_right_toolbar_space)
  1046. val xOff = (-contentView.measuredWidth - space).toInt()
  1047. val yOff = -contentView.measuredHeight
  1048. window.showAsDropDown(btn, xOff, yOff)
  1049. AnimationUtil.hideViewAlpha(view_ink_menu)
  1050. window.setOnDismissListener {
  1051. AnimationUtil.showViewAlpha(view_ink_menu)
  1052. }
  1053. }
  1054. return@setOnLongClickListener true
  1055. }
  1056. }
  1057. if (!isPdf()) {
  1058. iv_readerActivity_highLight.visibility = View.GONE
  1059. iv_readerActivity_strike.visibility = View.GONE
  1060. iv_readerActivity_underline.visibility = View.GONE
  1061. iv_readerActivity_ink.visibility = View.GONE
  1062. view_readerActivity_highLightStroke.visibility = View.GONE
  1063. view_readerActivity_strikeStroke.visibility = View.GONE
  1064. view_readerActivity_underlineStroke.visibility = View.GONE
  1065. }
  1066. }
  1067. private fun setLeftToolbarWidth(width: Int) {
  1068. var params = viewGroup_readerActivity_leftToolbar.layoutParams as ConstraintLayout.LayoutParams
  1069. params.width = width + w_left
  1070. params.height = 0
  1071. viewGroup_readerActivity_leftToolbar.layoutParams = params
  1072. params = linearLayout_readerActivity_leftToolbarContainer.layoutParams as ConstraintLayout.LayoutParams
  1073. params.width = width
  1074. linearLayout_readerActivity_leftToolbarContainer.layoutParams = params
  1075. }
  1076. private fun updateLeftToolbarOpenState(type: ReaderViewModel.LeftToolbarType) {
  1077. val isLeftToolbarOpen = viewModel.isLeftToolbarOpenLiveData.value ?: false
  1078. viewModel.setIsLeftToolbarOpen(!isLeftToolbarOpen || oldLeftToolbarType != type)
  1079. cloneOriginConstraintSet()
  1080. }
  1081. private fun onMediaBoxShown() {
  1082. hideAllToolbars()
  1083. isHideToolbar = true
  1084. }
  1085. private fun onMediaBoxDismissed() {
  1086. showAllToolbars()
  1087. isHideToolbar = false
  1088. viewModel.clearSelection()
  1089. }
  1090. private fun setLeftToolbarContentView(leftToolbarType: ReaderViewModel.LeftToolbarType) {
  1091. if (oldLeftToolbarType != leftToolbarType) {
  1092. linearLayout_readerActivity_leftToolbarContainer.removeAllViews()
  1093. val view = when (leftToolbarType) {
  1094. ReaderViewModel.LeftToolbarType.NONE,
  1095. ReaderViewModel.LeftToolbarType.THUMBNAIL -> {
  1096. val thumbnailView = ThumbnailView(this)
  1097. thumbnailView.setHandlers(viewModel.pdfInfoHandler, viewModel.thumbnailHandler)
  1098. thumbnailView
  1099. }
  1100. ReaderViewModel.LeftToolbarType.BOOKMARK -> {
  1101. val bookmarkView = BookmarkView(this)
  1102. bookmarkView.setHandlers(viewModel.pdfInfoHandler, viewModel.thumbnailHandler, viewModel.bookmarkHandler)
  1103. bookmarkView
  1104. }
  1105. ReaderViewModel.LeftToolbarType.OUTLINE -> {
  1106. val outlineView = OutlineView(this)
  1107. outlineView.setHandlers(viewModel.pdfInfoHandler, viewModel.outlineHandler)
  1108. outlineView
  1109. }
  1110. ReaderViewModel.LeftToolbarType.SEARCH -> {
  1111. val searchView = SearchView(this)
  1112. searchView.setHandlers(viewModel.pdfInfoHandler, viewModel.searchHandler)
  1113. searchView
  1114. }
  1115. }
  1116. linearLayout_readerActivity_leftToolbarContainer.addView(view as View)
  1117. oldLeftToolbarType = leftToolbarType
  1118. }
  1119. }
  1120. private fun getLeftToolbarContentWidth(): Int {
  1121. return when (viewModel.leftToolbarTypeLiveData.value == ReaderViewModel.LeftToolbarType.THUMBNAIL) {
  1122. true -> w_240
  1123. false -> w_318
  1124. }
  1125. }
  1126. private fun onLeftToolbarTypeChanged(leftToolbarType: ReaderViewModel.LeftToolbarType?) {
  1127. updateLeftToolbarIcons()
  1128. setLeftToolbarWidth(getLeftToolbarContentWidth())
  1129. cloneOriginConstraintSet()
  1130. if (leftToolbarType != null) {
  1131. setLeftToolbarContentView(leftToolbarType)
  1132. }
  1133. }
  1134. private fun onLeftToolbarOpenStateChanged(open: Boolean?) {
  1135. updateLeftToolbarIcons()
  1136. when (open == true) {
  1137. true -> viewGroup_readerActivity_leftToolbar.smoothScrollTo(0, 0, UI_ANIMATION_DURATION.toInt())
  1138. false -> {
  1139. viewGroup_readerActivity_leftToolbar.smoothScrollTo(getLeftToolbarContentWidth(), 0, UI_ANIMATION_DURATION.toInt())
  1140. hideSearchViewSoftKeyboard()
  1141. }
  1142. }
  1143. }
  1144. private fun updateLeftToolbarIcons() {
  1145. iv_readerActivity_thumbnail.setImageResource(R.drawable.ic_left_toolbar_thumbnails)
  1146. iv_readerActivity_bookmark.setImageResource(R.drawable.ic_left_toolbar_bookmarks)
  1147. iv_readerActivity_outline.setImageResource(R.drawable.ic_left_toolbar_outline)
  1148. iv_readerActivity_search.setImageResource(R.drawable.ic_left_toolbar_search)
  1149. if (viewModel.isLeftToolbarOpenLiveData.value == true) {
  1150. when (viewModel.leftToolbarTypeLiveData.value) {
  1151. ReaderViewModel.LeftToolbarType.NONE -> {}
  1152. ReaderViewModel.LeftToolbarType.THUMBNAIL -> iv_readerActivity_thumbnail.setImageResource(R.drawable.ic_left_toolbar_thumbnails_selected)
  1153. ReaderViewModel.LeftToolbarType.BOOKMARK -> iv_readerActivity_bookmark.setImageResource(R.drawable.ic_left_toolbar_bookmarks_selected)
  1154. ReaderViewModel.LeftToolbarType.OUTLINE -> iv_readerActivity_outline.setImageResource(R.drawable.ic_left_toolbar_outline_selected)
  1155. ReaderViewModel.LeftToolbarType.SEARCH -> iv_readerActivity_search.setImageResource(R.drawable.ic_left_toolbar_search_selected)
  1156. }
  1157. }
  1158. }
  1159. private fun onClickBottomThumb1() {
  1160. val thumbDialog = providePdfThumbDialogFragment()
  1161. thumbDialog.show(supportFragmentManager, FRAGMENT_PDF_THUMB_TAG)
  1162. }
  1163. override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
  1164. super.onActivityResult(requestCode, resultCode, data)
  1165. if (resultCode == Activity.RESULT_OK){
  1166. when (requestCode) {
  1167. REQUEST_PASSWORD -> {
  1168. val password = data?.getStringExtra(DialogPasswordActivity.KEY_USER_INPUT_PASSWORD) ?: ""
  1169. if (password.isEmpty()) {
  1170. finish()
  1171. } else {
  1172. val isOpened = viewModel.isOpenedFileLiveData.value ?: return
  1173. if (isOpened) return
  1174. val result = viewModel.openPdfFile(
  1175. this@ReaderActivity,
  1176. password,
  1177. Runnable { requestPassword(true) },
  1178. intent.type
  1179. )
  1180. if (result == ReaderViewModel.OpenFileResult.FAILED) {
  1181. showFileCannotOpenDialog(getString(R.string.cannot_open_file_Path, filePath))
  1182. }
  1183. }
  1184. }
  1185. }
  1186. }
  1187. }
  1188. private fun onEvent(event: ReaderViewModel.Event?) {
  1189. if (event == null) return
  1190. when (event) {
  1191. is ReaderViewModel.Event.OnFinishedHighlight -> HookEvent.OnFinishedHighlight()
  1192. is ReaderViewModel.Event.OnFinishedStrikeout -> HookEvent.OnFinishedStrikeout()
  1193. is ReaderViewModel.Event.OnFinishedUnderLine -> HookEvent.OnFinishedUnderline()
  1194. is ReaderViewModel.Event.OnFinishedInk -> HookEvent.OnFinishedInk()
  1195. }.also {
  1196. onEvent(it)
  1197. }
  1198. viewModel.onEventConsumed(event)
  1199. }
  1200. protected sealed class HookEvent {
  1201. class OnClickTopBookmarkBtn : HookEvent()
  1202. class OnClickBottomPreviousBtn : HookEvent()
  1203. class OnClickBottomNextBtn : HookEvent()
  1204. class OnClickBottomMediaBoxBtn : HookEvent()
  1205. class OnClickBottomThumbnailBtn : HookEvent()
  1206. class OnClickHighlightBtn : HookEvent()
  1207. class OnClickStrikeoutBtn : HookEvent()
  1208. class OnClickUnderlineBtn : HookEvent()
  1209. class OnClickInkBtn : HookEvent()
  1210. class OnLongClickHighlightBtn : HookEvent()
  1211. class OnLongClickStrikeoutBtn : HookEvent()
  1212. class OnLongClickUnderlineBtn : HookEvent()
  1213. class OnLongClickInkBtn : HookEvent()
  1214. class OnFinishedHighlight : HookEvent()
  1215. class OnFinishedStrikeout : HookEvent()
  1216. class OnFinishedUnderline : HookEvent()
  1217. class OnFinishedInk : HookEvent()
  1218. class OnUserChangeHighlightPropertySuccess : HookEvent()
  1219. class OnUserChangeStrikeoutPropertySuccess : HookEvent()
  1220. class OnUserChangeUnderlinePropertySuccess : HookEvent()
  1221. class OnUserChangeInkPropertySuccess : HookEvent()
  1222. }
  1223. }