Quellcode durchsuchen

Merge remote-tracking branch 'origin/master'

cooperku_kdanmobile vor 5 Jahren
Ursprung
Commit
1a5723ba46
100 geänderte Dateien mit 2283 neuen und 92 gelöschten Zeilen
  1. BIN
      app/src/main/jniLibs/arm64-v8a/libkmpdf.so
  2. BIN
      app/src/main/jniLibs/armeabi-v7a/libkmpdf.so
  3. BIN
      app/src/main/jniLibs/armeabi/libkmpdf.so
  4. BIN
      app/src/main/jniLibs/mips/libkmpdf.so
  5. BIN
      app/src/main/jniLibs/x86/libkmpdf.so
  6. 1 1
      reader/build.gradle
  7. BIN
      reader/libs/kmpdfkit-1.0.8.aar
  8. 4 0
      reader/src/main/AndroidManifest.xml
  9. 9 0
      reader/src/main/java/com/kdanmobile/reader/Config.kt
  10. 273 20
      reader/src/main/java/com/kdanmobile/reader/ReaderActivity.kt
  11. 195 28
      reader/src/main/java/com/kdanmobile/reader/ReaderViewModel.kt
  12. 10 0
      reader/src/main/java/com/kdanmobile/reader/Utils.kt
  13. 5 0
      reader/src/main/java/com/kdanmobile/reader/annotationattribute/AnnotationAttribute.kt
  14. 16 0
      reader/src/main/java/com/kdanmobile/reader/annotationattribute/AnnotationColor.kt
  15. 6 0
      reader/src/main/java/com/kdanmobile/reader/annotationattribute/Brush.kt
  16. 9 0
      reader/src/main/java/com/kdanmobile/reader/annotationattribute/InkAttribute.kt
  17. 146 0
      reader/src/main/java/com/kdanmobile/reader/screen/SignatureActivity.kt
  18. 5 0
      reader/src/main/java/com/kdanmobile/reader/screen/data/ShapeAttribute.kt
  19. 3 0
      reader/src/main/java/com/kdanmobile/reader/screen/data/SignatureAttribute.kt
  20. 11 0
      reader/src/main/java/com/kdanmobile/reader/screen/data/StampAttribute.kt
  21. 3 0
      reader/src/main/java/com/kdanmobile/reader/screen/data/TextBoxAttribute.kt
  22. 62 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/ColorOvalView.kt
  23. 91 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/ColorSelectView.kt
  24. 161 31
      reader/src/main/java/com/kdanmobile/reader/screen/view/ViewerEditView.kt
  25. 66 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/CustomStampAdapter.kt
  26. 19 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/CustomStampType.kt
  27. 5 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/OnSignatureAddListener.kt
  28. 5 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/OnSignatureClickListener.kt
  29. 5 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/OnSignatureRemoveListener.kt
  30. 5 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/OnStampClickListener.kt
  31. 138 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/ShapeTabView.kt
  32. 127 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/SignatureAdapter.kt
  33. 103 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/SignatureDrawView.kt
  34. 18 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/SignatureRecyclerViewItemDecoration.kt
  35. 101 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/SignatureTabView.kt
  36. 147 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/StampTabView.kt
  37. 46 0
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/StandardStampAdapter.kt
  38. 45 8
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/TextBoxTabView.kt
  39. 17 1
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/TextSeekBar.kt
  40. 6 2
      reader/src/main/java/com/kdanmobile/reader/screen/view/edit/TitleButton.kt
  41. 280 0
      reader/src/main/java/com/kdanmobile/reader/view/AnnotationAttributeView.kt
  42. 22 0
      reader/src/main/java/com/kdanmobile/reader/view/AnnotationAttributeWindow.kt
  43. 30 0
      reader/src/main/java/com/kdanmobile/reader/view/ColorOpacityDisplayerView.kt
  44. 1 1
      reader/src/main/java/com/kdanmobile/reader/HorizontalView.java
  45. 82 0
      reader/src/main/java/com/kdanmobile/reader/view/WaveLineView.java
  46. BIN
      reader/src/main/res/drawable-hdpi/ic_chevron_left.png
  47. BIN
      reader/src/main/res/drawable-hdpi/ic_chevron_right.png
  48. BIN
      reader/src/main/res/drawable-hdpi/ic_freehand_triangle.png
  49. BIN
      reader/src/main/res/drawable-hdpi/ic_kdan_cloud.png
  50. BIN
      reader/src/main/res/drawable-hdpi/ic_view_all.png
  51. BIN
      reader/src/main/res/drawable-hdpi/panel_property_color_preview.png
  52. BIN
      reader/src/main/res/drawable-hdpi/rectangle_pic_arrow.webp
  53. BIN
      reader/src/main/res/drawable-hdpi/rectangle_pic_circular.webp
  54. BIN
      reader/src/main/res/drawable-hdpi/rectangle_pic_line.webp
  55. BIN
      reader/src/main/res/drawable-hdpi/rectangle_pic_rectangle.webp
  56. BIN
      reader/src/main/res/drawable-hdpi/signature_ic_add.webp
  57. BIN
      reader/src/main/res/drawable-hdpi/stamp_blue_left_arrow.webp
  58. BIN
      reader/src/main/res/drawable-hdpi/stamp_blue_rect.webp
  59. BIN
      reader/src/main/res/drawable-hdpi/stamp_blue_right_arrow.webp
  60. BIN
      reader/src/main/res/drawable-hdpi/stamp_green_left_arrow.webp
  61. BIN
      reader/src/main/res/drawable-hdpi/stamp_green_rect.webp
  62. BIN
      reader/src/main/res/drawable-hdpi/stamp_green_right_arrow.webp
  63. BIN
      reader/src/main/res/drawable-hdpi/stamp_red_left_arrow.webp
  64. BIN
      reader/src/main/res/drawable-hdpi/stamp_red_rect.webp
  65. BIN
      reader/src/main/res/drawable-hdpi/stamp_red_right_arrow.webp
  66. BIN
      reader/src/main/res/drawable-mdpi/rectangle_pic_arrow.webp
  67. BIN
      reader/src/main/res/drawable-mdpi/rectangle_pic_circular.webp
  68. BIN
      reader/src/main/res/drawable-mdpi/rectangle_pic_line.webp
  69. BIN
      reader/src/main/res/drawable-mdpi/rectangle_pic_rectangle.webp
  70. BIN
      reader/src/main/res/drawable-mdpi/signature_ic_add.webp
  71. BIN
      reader/src/main/res/drawable-xhdpi/ic_chevron_left.png
  72. BIN
      reader/src/main/res/drawable-xhdpi/ic_chevron_right.png
  73. BIN
      reader/src/main/res/drawable-xhdpi/ic_kdan_cloud.png
  74. BIN
      reader/src/main/res/drawable-xhdpi/ic_view_all.png
  75. BIN
      reader/src/main/res/drawable-xhdpi/panel_property_color_preview.png
  76. BIN
      reader/src/main/res/drawable-xhdpi/rectangle_pic_arrow.webp
  77. BIN
      reader/src/main/res/drawable-xhdpi/rectangle_pic_circular.webp
  78. BIN
      reader/src/main/res/drawable-xhdpi/rectangle_pic_line.webp
  79. BIN
      reader/src/main/res/drawable-xhdpi/rectangle_pic_rectangle.webp
  80. BIN
      reader/src/main/res/drawable-xhdpi/signature_ic_add.webp
  81. BIN
      reader/src/main/res/drawable-xhdpi/stamp_blue_left_arrow.webp
  82. BIN
      reader/src/main/res/drawable-xhdpi/stamp_blue_rect.webp
  83. BIN
      reader/src/main/res/drawable-xhdpi/stamp_blue_right_arrow.webp
  84. BIN
      reader/src/main/res/drawable-xhdpi/stamp_green_left_arrow.webp
  85. BIN
      reader/src/main/res/drawable-xhdpi/stamp_green_rect.webp
  86. BIN
      reader/src/main/res/drawable-xhdpi/stamp_green_right_arrow.webp
  87. BIN
      reader/src/main/res/drawable-xhdpi/stamp_red_left_arrow.webp
  88. BIN
      reader/src/main/res/drawable-xhdpi/stamp_red_rect.webp
  89. BIN
      reader/src/main/res/drawable-xhdpi/stamp_red_right_arrow.webp
  90. 5 0
      reader/src/main/res/drawable-xxhdpi/bg_annotation_property_setting_window.xml
  91. BIN
      reader/src/main/res/drawable-xxhdpi/ic_chevron_left.png
  92. BIN
      reader/src/main/res/drawable-xxhdpi/ic_chevron_right.png
  93. BIN
      reader/src/main/res/drawable-xxhdpi/ic_freehand_triangle.png
  94. BIN
      reader/src/main/res/drawable-xxhdpi/ic_kdan_cloud.png
  95. BIN
      reader/src/main/res/drawable-xxhdpi/ic_view_all.png
  96. BIN
      reader/src/main/res/drawable-xxhdpi/rectangle_pic_arrow.webp
  97. BIN
      reader/src/main/res/drawable-xxhdpi/rectangle_pic_circular.webp
  98. BIN
      reader/src/main/res/drawable-xxhdpi/rectangle_pic_line.webp
  99. BIN
      reader/src/main/res/drawable-xxhdpi/rectangle_pic_rectangle.webp
  100. 0 0
      reader/src/main/res/drawable-xxhdpi/seekbar_back.png

BIN
app/src/main/jniLibs/arm64-v8a/libkmpdf.so


BIN
app/src/main/jniLibs/armeabi-v7a/libkmpdf.so


BIN
app/src/main/jniLibs/armeabi/libkmpdf.so


BIN
app/src/main/jniLibs/mips/libkmpdf.so


BIN
app/src/main/jniLibs/x86/libkmpdf.so


+ 1 - 1
reader/build.gradle

@@ -46,5 +46,5 @@ dependencies {
 
     testImplementation 'junit:junit:4.12'
 
-    api(name: 'kmpdfkit-1.0.8', ext: 'aar')
+    api(name: 'kmpdfkit-1.0.8.1', ext: 'aar')
 }

BIN
reader/libs/kmpdfkit-1.0.8.aar


+ 4 - 0
reader/src/main/AndroidManifest.xml

@@ -9,6 +9,10 @@
         <activity
             android:name=".screen.ViewerSettingActivity"
             android:theme="@style/ReaderActivityNoActionBarNoTitle" />
+        <activity
+            android:name=".screen.SignatureActivity"
+            android:screenOrientation="landscape"
+            android:theme="@style/ReaderActivityNoActionBarNoTitle" />
     </application>
 
 </manifest>

+ 9 - 0
reader/src/main/java/com/kdanmobile/reader/Config.kt

@@ -1,6 +1,15 @@
 package com.kdanmobile.reader
 
+import android.content.Context
+import java.io.File
+
 object Config {
     var PDF_SDK_LICENSE = ""
     var PDF_SDK_RSA_MSG = ""
+
+    const val SIGNATURE_FOLDER = "Signature"
+
+    fun getSignatureDirectoryPath(context: Context) : String {
+        return "${context.filesDir.absolutePath}${File.separator}$SIGNATURE_FOLDER"
+    }
 }

+ 273 - 20
reader/src/main/java/com/kdanmobile/reader/ReaderActivity.kt

@@ -6,31 +6,41 @@ import android.arch.lifecycle.ViewModelProviders
 import android.content.Intent
 import android.net.Uri
 import android.os.Build
-import android.support.v7.app.AppCompatActivity
 import android.os.Bundle
 import android.support.constraint.ConstraintLayout
 import android.support.constraint.ConstraintSet
+import android.widget.Toast
+import com.kdanmobile.kmpdfkit.pdfcommon.KMPDFReaderView
+import com.kdanmobile.reader.Utils.applyConstraintSet
+import com.kdanmobile.reader.view.AnnotationAttributeView
+import com.kdanmobile.reader.view.AnnotationAttributeWindow
 import android.support.v4.content.ContextCompat
 import android.support.v7.app.AlertDialog
+import android.support.v7.app.AppCompatActivity
+import android.util.DisplayMetrics
 import android.view.Menu
 import android.view.MenuItem
-import android.util.DisplayMetrics
 import android.view.MotionEvent
 import android.view.View
 import android.widget.EditText
 import android.widget.LinearLayout
 import android.widget.TextView
-import com.kdanmobile.kmpdfkit.pdfcommon.KMPDFReaderView
-import com.kdanmobile.reader.Utils.applyConstraintSet
+import com.kdanmobile.reader.annotationattribute.AnnotationAttribute
+import com.kdanmobile.reader.annotationattribute.AnnotationColor
+import com.kdanmobile.reader.annotationattribute.InkAttribute
 import com.kdanmobile.reader.screen.view.BookmarkView
 import com.kdanmobile.reader.screen.view.OutlineView
 import com.kdanmobile.reader.screen.view.SearchView
 import com.kdanmobile.reader.screen.view.ThumbnailView
+import com.kdanmobile.reader.screen.ViewerSettingActivity
+import com.kdanmobile.reader.screen.view.*
+import com.kdanmobile.reader.screen.view.edit.ShapeTabView
+import com.kdanmobile.reader.screen.view.edit.SignatureTabView
+import com.kdanmobile.reader.screen.view.edit.StampTabView
+import com.kdanmobile.reader.screen.view.edit.TextBoxTabView
 import com.kdanmobile.reader.utils.AnimationUtil
 import com.kdanmobile.reader.utils.DensityUtil
-import com.kdanmobile.reader.screen.ViewerSettingActivity
 import kotlinx.android.synthetic.main.activity_reader.*
-import kotlin.Exception
 
 abstract class ReaderActivity : AppCompatActivity() {
 
@@ -82,21 +92,36 @@ abstract class ReaderActivity : AppCompatActivity() {
         setupLeftToolbar()
         setupRightSideToolbar()
         setupBottomToolbar()
+        setupViewerEditView()
 
         val factory = ReaderViewModelFactory()
         viewModel = ViewModelProviders.of(this, factory).get(ReaderViewModel::class.java)
         viewModel.isOpenedFileLiveData.observe(this, Observer(this::onIsOpenedFileUpdate))
         viewModel.fileNameLiveData.observe(this, Observer { tv_readerActivity_title.text = it })
+        viewModel.annotationModeLiveData.observe(this, Observer(this::onAnnotationModeUpdate))
+        viewModel.isCopyModeLiveData.observe(this, Observer (this::onIsCopyModeUpdate))
+        viewModel.highLightAttributeLiveData.observe(this, Observer(this::onHighlightAttributeUpdate))
+        viewModel.strikeAttributeLiveData.observe(this, Observer(this::onStrikeAttributeUpdate))
+        viewModel.underLineAttributeLiveData.observe(this, Observer(this::onUnderlineAttributeUpdate))
+        viewModel.inkAttributeLiveData.observe(this, Observer(this::onInkAttributeUpdate))
         viewModel.isOpenedFileLiveData.value?.also { isOpened ->
             if (isOpened) return@also
             val filePath = intent.getStringExtra(KEY_FILE_ABSOLUTE)
             val uri = Uri.parse(filePath)
             viewModel.openPdfFile(this, uri, intent.type)
         }
+        setupRightToolbar()
         viewModel.pageIndexLiveData.observe(this, Observer(this::onPageIndexChanged))
         viewModel.isPageInBookmarksLiveData.observe(this, Observer(this::onIsPageInBookmarksChanged))
     }
 
+    override fun onBackPressed() {
+        when {
+            viewerEditView.isShown -> viewerEditView.dismiss()
+            else -> super.onBackPressed()
+        }
+    }
+
     override fun onCreateOptionsMenu(menu: Menu?): Boolean {
         menuInflater.inflate(R.menu.reader_more, menu)
         menuItemBookmark = menu?.findItem(R.id.item_reader_bookmark)
@@ -139,6 +164,81 @@ abstract class ReaderActivity : AppCompatActivity() {
         }
     }
 
+    private fun onIsCopyModeUpdate(isCopyMode: Boolean?) {
+        if (isCopyMode == null) return
+        when (isCopyMode) {
+            true -> {
+                val color = resources.getColor(R.color.reader_right_toolbar_selected_bg)
+                iv_readerActivity_copy.setBackgroundColor(color)
+                hideTopLeftBottomToolbars()
+            }
+            false -> {
+                iv_readerActivity_copy.setBackgroundDrawable(null)
+                showAllToolbars()
+            }
+        }
+    }
+
+    private fun onHighlightAttributeUpdate(annotationAttribute: AnnotationAttribute?) {
+        if (annotationAttribute == null) return
+        view_readerActivity_highLightStroke.setBackgroundColor(annotationAttribute.color)
+    }
+
+    private fun onStrikeAttributeUpdate(annotationAttribute: AnnotationAttribute?) {
+        if (annotationAttribute == null) return
+        view_readerActivity_strikeStroke.setBackgroundColor(annotationAttribute.color)
+    }
+
+    private fun onUnderlineAttributeUpdate(annotationAttribute: AnnotationAttribute?) {
+        if (annotationAttribute == null) return
+        view_readerActivity_underlineStroke.setBackgroundColor(annotationAttribute.color)
+    }
+
+    private fun onInkAttributeUpdate(inkAttribute: InkAttribute?) {
+        if (inkAttribute == null) return
+        val color = inkAttribute.color
+        AnnotationColor.values().forEach {
+            if (resources.getColor(it.colorResId) == color) {
+                val drawableResId = when (it) {
+                    AnnotationColor.Color1 -> R.drawable.ic_tool_freehand_s1
+                    AnnotationColor.Color2 -> R.drawable.ic_tool_freehand_s2
+                    AnnotationColor.Color3 -> R.drawable.ic_tool_freehand_s3
+                    AnnotationColor.Color4 -> R.drawable.ic_tool_freehand_s4
+                    AnnotationColor.Color5 -> R.drawable.ic_tool_freehand_s5
+                    AnnotationColor.Color6 -> R.drawable.ic_tool_freehand_s6
+                    AnnotationColor.Color7 -> R.drawable.ic_tool_freehand_s7
+                    AnnotationColor.Color8 -> R.drawable.ic_tool_freehand_s8
+                    AnnotationColor.Color9 -> R.drawable.ic_tool_freehand_s9
+                    AnnotationColor.Color10 -> R.drawable.ic_tool_freehand_s10
+                }
+                iv_readerActivity_inkStroke.setImageResource(drawableResId)
+                return
+            }
+        }
+    }
+
+    private fun onAnnotationModeUpdate(annotationMode: ReaderViewModel.AnnotationMode?) {
+        if (annotationMode == null) return
+        val map = HashMap<ReaderViewModel.AnnotationMode, View>().apply {
+            put(ReaderViewModel.AnnotationMode.Highlight, iv_readerActivity_highLight)
+            put(ReaderViewModel.AnnotationMode.Strike, iv_readerActivity_strike)
+            put(ReaderViewModel.AnnotationMode.Underline, iv_readerActivity_underline)
+            put(ReaderViewModel.AnnotationMode.Ink, iv_readerActivity_ink)
+        }
+        map.forEach {
+            if (it.key == annotationMode) {
+                val color = resources.getColor(R.color.reader_right_toolbar_selected_bg)
+                it.value.setBackgroundColor(color)
+            } else {
+                it.value.setBackgroundDrawable(null)
+            }
+        }
+        when (annotationMode == ReaderViewModel.AnnotationMode.None) {
+            true -> showAllToolbars()
+            false -> hideTopLeftBottomToolbars()
+        }
+    }
+
     private fun onIsOpenedFileUpdate(isOpened: Boolean?) {
         if (isOpened == null) return
         val container = viewGroup_readerActivity_container
@@ -146,6 +246,19 @@ abstract class ReaderActivity : AppCompatActivity() {
         if (!isOpened) return
         val context = this
         val readerView = object : KMPDFReaderView(context) {
+            @SuppressLint("ClickableViewAccessibility")
+            override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
+                if (motionEvent.action == MotionEvent.ACTION_UP) {
+                    if (viewModel.isCopyModeLiveData.value == true) {
+                        if (viewModel.copySelection()) {
+                            val context = this@ReaderActivity
+                            Toast.makeText(context, R.string.reader_copy_text_success, Toast.LENGTH_SHORT).show()
+                        }
+                    }
+                }
+                return super.onTouchEvent(motionEvent)
+            }
+
             override fun onTapMainDocArea() {
                 super.onTapMainDocArea()
                 if (isBelowKitkat()) {
@@ -161,9 +274,9 @@ abstract class ReaderActivity : AppCompatActivity() {
                 isHideToolbar = !isHideToolbar
             }
 
-            override fun onMoveToChild(p0: Int) {
-                super.onMoveToChild(p0)
-                viewModel.setPageIndex(p0)
+            override fun onMoveToChild(pageIndex: Int) {
+                super.onMoveToChild(pageIndex)
+                viewModel.setPageIndex(pageIndex)
                 updateThumbnailViewSelectedPage()
             }
 
@@ -350,17 +463,150 @@ abstract class ReaderActivity : AppCompatActivity() {
     }
 
     private fun setupBottomToolbar() {
-        val context = this
-        /** Set buttons color **/
-        val normalColor = R.color.reader_bottom_toolbar_bottom_color_normal
-        val pressColor = R.color.reader_bottom_toolbar_bottom_color_press
-        Utils.setTintDrawableList(context, ib_readerActivity_bottomToolbarPrevious, R.drawable.ic_chevron_left, normalColor, pressColor)
-        Utils.setTintDrawableList(context, ib_readerActivity_bottomToolbarNext, R.drawable.ic_chevron_right, normalColor, pressColor)
-        Utils.setTintDrawableList(context, ib_readerActivity_bottomToolbarBookmark, R.drawable.ic_bookmark_border, normalColor, pressColor)
-        Utils.setTintDrawableList(context, ib_readerActivity_bottomToolbarViewAll, R.drawable.ic_view_all, normalColor, pressColor)
-        Utils.setTintDrawableList(context, ib_readerActivity_bottomToolbarKdanCloud, R.drawable.ic_kdan_cloud, normalColor, pressColor)
+        ib_readerActivity_bottomToolbarMediaBox.setOnClickListener { viewerEditView.show() }
     }
 
+    private fun setupViewerEditView() {
+        viewerEditView.apply {
+            onShowListener = Runnable { onMediaBoxShown() }
+            onDismissListener = Runnable { onMediaBoxDismissed() }
+            onViewerEditTabAddButtonClickListener = object : ViewerEditView.OnViewerEditTabAddButtonClickListener {
+                override fun onTabTextBoxAddButtonClick(textBoxTabView: TextBoxTabView) {
+                    hideAllToolbars()
+                    viewModel.setTextBoxAttribute(textBoxTabView.getTextBoxAttribute())
+                }
+
+                override fun onTabSignatureAddButtonClick(signatureTabView: SignatureTabView) {
+                    hideAllToolbars()
+                    viewModel.setSignatureAttribute(signatureTabView.getSignatureAttribute())
+                }
+
+                override fun onTabStampAddButtonClick(stampTabView: StampTabView) {
+                    hideAllToolbars()
+                    viewModel.setStampAttribute(stampTabView.getStampAttribute())
+                }
+
+                override fun onTabShapeAddButtonClick(shapeTabView: ShapeTabView) {
+                    hideAllToolbars()
+                    viewModel.setShapeAttribute(shapeTabView.getShapeAttribute())
+                }
+            }
+        }
+    }
+
+    private fun setupRightToolbar() {
+        iv_readerActivity_copy.setOnClickListener { viewModel.onClickCopyBtn() }
+        iv_readerActivity_highLight.apply {
+            setOnClickListener { viewModel.onClickHighlightBtn() }
+            setOnLongClickListener { btn ->
+                viewModel.onLongClickHighlightBtn()
+                val context = this@ReaderActivity
+                AnnotationAttributeWindow(context, true).also { window ->
+                    viewModel.highLightAttributeLiveData.value?.let { attr ->
+                        window.annotationAttributeView.annotationAttribute = attr
+                    }
+                    window.annotationAttributeView.onChangeListener = object : AnnotationAttributeView.OnChangeListener {
+                        override fun onChange(annotationAttributeView: AnnotationAttributeView) {
+                            viewModel.setHighLightAttributes(annotationAttributeView.annotationAttribute)
+                        }
+                    }
+                    val contentView = window.contentView.apply {
+                        val w = Utils.makeDropDownMeasureSpec(window.width)
+                        val y = Utils.makeDropDownMeasureSpec(window.height)
+                        measure(w, y)
+                    }
+                    val space = resources.getDimension(R.dimen.reader_annotation_property_setting_window_right_toolbar_space)
+                    val xOff = (-contentView.measuredWidth - space).toInt()
+                    val yOff = -btn.height
+                    window.showAsDropDown(btn, xOff, yOff)
+                }
+                return@setOnLongClickListener true
+            }
+        }
+        iv_readerActivity_strike.apply {
+            setOnClickListener { viewModel.onClickStrikeBtn() }
+            setOnLongClickListener { btn ->
+                viewModel.onLongClickStrikeBtn()
+                val context = this@ReaderActivity
+                AnnotationAttributeWindow(context, true).also { window ->
+                    viewModel.strikeAttributeLiveData.value?.let { attr ->
+                        window.annotationAttributeView.annotationAttribute = attr
+                    }
+                    window.annotationAttributeView.onChangeListener = object : AnnotationAttributeView.OnChangeListener {
+                        override fun onChange(annotationAttributeView: AnnotationAttributeView) {
+                            viewModel.setStrikeOutAttributes(annotationAttributeView.annotationAttribute)
+                        }
+                    }
+                    val contentView = window.contentView.apply {
+                        val w = Utils.makeDropDownMeasureSpec(window.width)
+                        val y = Utils.makeDropDownMeasureSpec(window.height)
+                        measure(w, y)
+                    }
+                    val space = resources.getDimension(R.dimen.reader_annotation_property_setting_window_right_toolbar_space)
+                    val xOff = (-contentView.measuredWidth - space).toInt()
+                    val yOff = -btn.height
+                    window.showAsDropDown(btn, xOff, yOff)
+                }
+                return@setOnLongClickListener true
+            }
+        }
+        iv_readerActivity_underline.apply {
+            setOnClickListener { viewModel.onClickUnderlineBtn() }
+            setOnLongClickListener { btn ->
+                viewModel.onLongClickUnderlineBtn()
+                val context = this@ReaderActivity
+                AnnotationAttributeWindow(context, true).also { window ->
+                    viewModel.underLineAttributeLiveData.value?.let { attr ->
+                        window.annotationAttributeView.annotationAttribute = attr
+                    }
+                    window.annotationAttributeView.onChangeListener = object : AnnotationAttributeView.OnChangeListener {
+                        override fun onChange(annotationAttributeView: AnnotationAttributeView) {
+                            viewModel.setUnderLineAttributes(annotationAttributeView.annotationAttribute)
+                        }
+                    }
+                    val contentView = window.contentView.apply {
+                        val w = Utils.makeDropDownMeasureSpec(window.width)
+                        val y = Utils.makeDropDownMeasureSpec(window.height)
+                        measure(w, y)
+                    }
+                    val space = resources.getDimension(R.dimen.reader_annotation_property_setting_window_right_toolbar_space)
+                    val xOff = (-contentView.measuredWidth - space).toInt()
+                    val yOff = -btn.height
+                    window.showAsDropDown(btn, xOff, yOff)
+                }
+                return@setOnLongClickListener true
+            }
+        }
+        iv_readerActivity_ink.apply {
+            setOnClickListener { viewModel.onClickInkBtn() }
+            setOnLongClickListener { btn ->
+                viewModel.onLongClickInkBtn()
+                val context = this@ReaderActivity
+                AnnotationAttributeWindow(context, false).also { window ->
+                    viewModel.inkAttributeLiveData.value?.let { attr ->
+                        window.annotationAttributeView.inkAttribute = attr
+                    }
+                    window.annotationAttributeView.onChangeListener = object : AnnotationAttributeView.OnChangeListener {
+                        override fun onChange(annotationAttributeView: AnnotationAttributeView) {
+                            viewModel.setInkAttributes(annotationAttributeView.inkAttribute)
+                        }
+                    }
+                    val contentView = window.contentView.apply {
+                        val w = Utils.makeDropDownMeasureSpec(window.width)
+                        val y = Utils.makeDropDownMeasureSpec(window.height)
+                        measure(w, y)
+                    }
+                    val space = resources.getDimension(R.dimen.reader_annotation_property_setting_window_right_toolbar_space)
+                    val xOff = (-contentView.measuredWidth - space).toInt()
+                    val yOff = -contentView.measuredHeight
+                    window.showAsDropDown(btn, xOff, yOff)
+                }
+                return@setOnLongClickListener true
+            }
+        }
+    }
+
+
     private fun setLeftToolbarWidth(width: Int) {
         var params = viewGroup_readerActivity_leftToolbar.layoutParams as ConstraintLayout.LayoutParams
         params.width = width + w_left
@@ -392,6 +638,14 @@ abstract class ReaderActivity : AppCompatActivity() {
         cloneOriginConstraintSet()
     }
 
+    private fun onMediaBoxShown() {
+        hideAllToolbars()
+    }
+
+    private fun onMediaBoxDismissed() {
+        showAllToolbars()
+    }
+
     private fun onThumbnailClick() {
         val type = LeftToolbarType.THUMBNAIL
         changeLeftToolbarType(type)
@@ -461,8 +715,7 @@ abstract class ReaderActivity : AppCompatActivity() {
         leftToolbarType = type
     }
 
-    fun onClick(view: View)
-    {
+    fun onClick(view: View) {
         when (view.id) {
             R.id.iv_readerActivity_thumbnail -> {
                 onThumbnailClick()

+ 195 - 28
reader/src/main/java/com/kdanmobile/reader/ReaderViewModel.kt

@@ -4,23 +4,54 @@ import android.arch.lifecycle.LiveData
 import android.arch.lifecycle.MutableLiveData
 import android.arch.lifecycle.ViewModel
 import android.content.Context
+import android.graphics.Color
 import android.graphics.Bitmap
 import android.graphics.RectF
 import android.net.Uri
+import com.kdanmobile.kmpdfkit.annotation.Annotation
 import com.kdanmobile.kmpdfkit.annotation.bean.*
+import com.kdanmobile.kmpdfkit.globaldata.AnnotConfig
 import com.kdanmobile.kmpdfkit.globaldata.Config
+import com.kdanmobile.kmpdfkit.globaldata.KMPDFAnnotEditMode
 import com.kdanmobile.kmpdfkit.manager.KMPDFFactory
 import com.kdanmobile.kmpdfkit.manager.controller.KMPDFDocumentController
+import com.kdanmobile.kmpdfkit.pdfcommon.KMPDFReaderView
+import com.kdanmobile.reader.annotationattribute.AnnotationAttribute
+import com.kdanmobile.reader.annotationattribute.InkAttribute
 import com.kdanmobile.kmpdfkit.pdfcommon.*
+import com.kdanmobile.reader.annotationattribute.Brush
+import com.kdanmobile.kmpdfkit.manager.controller.*
+import com.kdanmobile.kmpdfkit.manager.listener.KMPDFAddAnnotCallback
+import com.kdanmobile.kmpdfkit.pdfcommon.*
+import com.kdanmobile.reader.screen.data.ShapeAttribute
+import com.kdanmobile.reader.screen.data.SignatureAttribute
+import com.kdanmobile.reader.screen.data.StampAttribute
+import com.kdanmobile.reader.screen.data.TextBoxAttribute
 import com.kdanmobile.reader.screen.handler.*
 
 class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMsg: String) : ViewModel() {
+
     enum class ViewDirection(val mode: Config.PDFViewMode) {
         VerticalSinglePageContinues(Config.PDFViewMode.VERTICAL_SINGLE_PAGE_CONTINUES),
         VerticalSinglePage(Config.PDFViewMode.VERTICAL_SINGLE_PAGE),
         HorizontalSinglePage(Config.PDFViewMode.HORIZONTAL_SINGLE_PAGE)
     }
 
+    enum class AnnotationMode {
+        None,
+        Highlight,
+        Strike,
+        Underline,
+        Ink,
+    }
+
+    companion object {
+        val DEFAULT_HIGHLIGHT_ATTR = AnnotationAttribute(Color.parseColor("#ffdc1b"), 127)
+        val DEFAULT_STRIKE_ATTR = AnnotationAttribute(Color.parseColor("#dd0202"), 255)
+        val DEFAULT_UNDERLINE_ATTR = AnnotationAttribute(Color.parseColor("#dd0202"), 255)
+        val DEFAULT_INK_ATTR = InkAttribute(Color.parseColor("#dd0202"), 255, 1f, Brush.Fountain)
+    }
+
     var viewDirection = ViewDirection.VerticalSinglePageContinues
         set(value) {
             field = value
@@ -31,8 +62,10 @@ class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMs
             field = value
             updateCrop()
         }
-    var isCopyMode = false
-        private set
+    val highLightAttributeLiveData = MutableLiveData<AnnotationAttribute>().apply { value = DEFAULT_HIGHLIGHT_ATTR }
+    val strikeAttributeLiveData = MutableLiveData<AnnotationAttribute>().apply { value = DEFAULT_STRIKE_ATTR }
+    var underLineAttributeLiveData = MutableLiveData<AnnotationAttribute>().apply { value = DEFAULT_UNDERLINE_ATTR }
+    var inkAttributeLiveData = MutableLiveData<InkAttribute>().apply { value = DEFAULT_INK_ATTR }
 
     private var isVerified = false
 
@@ -40,6 +73,8 @@ class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMs
     private var kmpdfDocumentController: KMPDFDocumentController? = null
     val isOpenedFileLiveData = MutableLiveData<Boolean>().apply { value = false }
     val fileNameLiveData = MutableLiveData<String>()
+    val annotationModeLiveData = MutableLiveData<AnnotationMode>().apply { value = AnnotationMode.None }
+    var isCopyModeLiveData = MutableLiveData<Boolean>().apply { value = false }
 
     val pageIndexLiveData: LiveData<Int>
         get() = mPageIndexLiveData
@@ -85,75 +120,169 @@ class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMs
         return kmpdfDocumentController?.copySelection() ?: false
     }
 
-    fun startCopyTextMode() {
+    fun onClickCopyBtn() {
+        if (annotationModeLiveData.value != AnnotationMode.None) stopAnnotationMode()
+        if (isCopyModeLiveData.value == true) {
+            stopCopyTextMode()
+        } else {
+            startCopyTextMode()
+        }
+    }
+
+    fun onClickHighlightBtn() {
+        if (isCopyModeLiveData.value == true) stopCopyTextMode()
+        if (annotationModeLiveData.value == AnnotationMode.Highlight) {
+            stopAnnotationMode()
+        } else {
+            startHighLightEditMode()
+        }
+    }
+
+    fun onLongClickHighlightBtn() {
+        if (isCopyModeLiveData.value == true) stopCopyTextMode()
+        startHighLightEditMode()
+    }
+
+    fun onClickStrikeBtn() {
+        if (isCopyModeLiveData.value == true) stopCopyTextMode()
+        if (annotationModeLiveData.value == AnnotationMode.Strike) {
+            stopAnnotationMode()
+        } else {
+            startStrikeOutEditMode()
+        }
+    }
+
+    fun onLongClickStrikeBtn() {
+        if (isCopyModeLiveData.value == true) stopCopyTextMode()
+        startStrikeOutEditMode()
+    }
+
+    fun onClickUnderlineBtn() {
+        if (isCopyModeLiveData.value == true) stopCopyTextMode()
+        if (annotationModeLiveData.value == AnnotationMode.Underline) {
+            stopAnnotationMode()
+        } else {
+            startUnderLineEditMode()
+        }
+    }
+
+    fun onLongClickUnderlineBtn() {
+        if (isCopyModeLiveData.value == true) stopCopyTextMode()
+        startUnderLineEditMode()
+    }
+
+    fun onClickInkBtn() {
+        if (isCopyModeLiveData.value == true) stopCopyTextMode()
+        if (annotationModeLiveData.value == AnnotationMode.Ink) {
+            stopAnnotationMode()
+        } else {
+            startInkEditMode()
+        }
+    }
+
+    fun onLongClickInkBtn() {
+        if (isCopyModeLiveData.value == true) stopCopyTextMode()
+        startInkEditMode()
+    }
+
+    private fun startCopyTextMode() {
         kmpdfDocumentController?.startCopyText()
-        isCopyMode = true
+        isCopyModeLiveData.postValue(true)
     }
 
-    fun stopCopyTextMode() {
+    private fun stopCopyTextMode() {
         kmpdfDocumentController?.stopCopyText()
-        isCopyMode = false
+        isCopyModeLiveData.postValue(false)
     }
 
-    fun setHighLightAttributes(color: Int, alpha: Int) {
-        kmpdfFactory?.setAnnotationAttribute(KMPDFHighlightAnnotationBean("", color, alpha))
+    fun setHighLightAttributes(annotationAttribute: AnnotationAttribute) {
+        highLightAttributeLiveData.postValue(annotationAttribute)
+        kmpdfFactory?.setAnnotationAttribute(KMPDFHighlightAnnotationBean("", annotationAttribute.color, annotationAttribute.alpha))
         kmpdfFactory?.annotConfig?.apply {
-            markerPenColor_hightlight = color
-            markerPenAlpha_hightlight = alpha
+            markerPenColor_hightlight = annotationAttribute.color
+            markerPenAlpha_hightlight = annotationAttribute.alpha
         }
     }
 
-    fun startHighLightEditMode() {
+    private fun startHighLightEditMode() {
         kmpdfFactory?.setAnnotationEditMode(KMPDFAnnotationBean.AnnotationType.HIGH_LIGHT)
+        annotationModeLiveData.postValue(AnnotationMode.Highlight)
     }
 
-    fun setStrikeOutAttributes(color: Int, alpha: Int) {
-        kmpdfFactory?.setAnnotationAttribute(KMPDFStrikeoutAnnotationBean("", color, alpha))
+    fun setStrikeOutAttributes(annotationAttribute: AnnotationAttribute) {
+        strikeAttributeLiveData.postValue(annotationAttribute)
+        kmpdfFactory?.setAnnotationAttribute(KMPDFStrikeoutAnnotationBean("",annotationAttribute.color, annotationAttribute.alpha))
         kmpdfFactory?.annotConfig?.apply {
-            markerPenColor_strikeout = color
-            markerPenAlpha_strikeout = alpha
+            markerPenColor_strikeout = annotationAttribute.color
+            markerPenAlpha_strikeout = annotationAttribute.alpha
         }
     }
 
-    fun startStrikeOutEditMode() {
+    private fun startStrikeOutEditMode() {
         kmpdfFactory?.setAnnotationEditMode(KMPDFAnnotationBean.AnnotationType.STRIKE_OUT)
+        annotationModeLiveData.postValue(AnnotationMode.Strike)
     }
 
-    fun setUnderLineAttributes(color: Int, alpha: Int) {
-        kmpdfFactory?.setAnnotationAttribute(KMPDFUnderlineAnnotationBean("", color, alpha))
+    fun setUnderLineAttributes(annotationAttribute: AnnotationAttribute) {
+        underLineAttributeLiveData.postValue(annotationAttribute)
+        kmpdfFactory?.setAnnotationAttribute(KMPDFUnderlineAnnotationBean("", annotationAttribute.color, annotationAttribute.alpha))
         kmpdfFactory?.annotConfig?.apply {
-            markerPenColor_underline = color
-            markerPenAlpha_underline = alpha
+            markerPenColor_underline = annotationAttribute.color
+            markerPenAlpha_underline = annotationAttribute.alpha
         }
     }
 
-    fun startUnderLineEditMode() {
+    private fun startUnderLineEditMode() {
         kmpdfFactory?.setAnnotationEditMode(KMPDFAnnotationBean.AnnotationType.UNDER_LINE)
+        annotationModeLiveData.postValue(AnnotationMode.Underline)
     }
 
-    fun setInkAttributes(color: Int, alpha: Int, width: Float) {
+    fun setInkAttributes(inkAttribute: InkAttribute) {
+        inkAttributeLiveData.postValue(inkAttribute)
         kmpdfFactory?.apply {
-            setAnnotationAttribute(KMPDFInkAnnotationBean("", color, width, alpha))
+            setAnnotationAttribute(KMPDFInkAnnotationBean("", inkAttribute.color, inkAttribute.width, inkAttribute.alpha))
             annotConfig?.apply {
-                markerPenColor_ink = color
-                markerPenAlpha_ink = alpha
-                markerPenSize_ink = width
+                markerPenColor_ink = inkAttribute.color
+                markerPenAlpha_ink = inkAttribute.alpha
+                markerPenSize_ink = inkAttribute.width
             }
         }
     }
 
-    fun startInkEditMode() {
+    private fun startInkEditMode() {
         kmpdfFactory?.setAnnotationEditMode(KMPDFAnnotationBean.AnnotationType.INK)
+        annotationModeLiveData.postValue(AnnotationMode.Ink)
     }
 
-    fun stopEditMode() {
+    private fun stopAnnotationMode() {
         kmpdfFactory?.setAnnotationEditMode(KMPDFAnnotationBean.AnnotationType.NULL)
+        annotationModeLiveData.postValue(AnnotationMode.None)
     }
 
     fun setReaderView(readerView: KMPDFReaderView) {
         kmpdfFactory?.readerView = readerView
         kmpdfDocumentController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.DOCUMENT) as KMPDFDocumentController
         updateViewDirection()
+
+        kmpdfFactory?.kmpdfAddAnnotCallback = object : KMPDFAddAnnotCallback {
+
+            override fun onSaveAnnotFinished(type: Annotation.Type, result: Boolean, message: String) {
+                println("KMPDFAddAnnotCallback::onSaveAnnotFinished")
+            }
+
+            override fun onAttachAnnotWidgetFinished(type: Annotation.Type) {
+                println("KMPDFAddAnnotCallback::onAttachAnnotWidgetFinished")
+                val mode = when (type) {
+                    Annotation.Type.FREETEXT -> KMPDFAnnotEditMode.Mode.FREETEXT_MODIFY
+                    Annotation.Type.STAMP -> KMPDFAnnotEditMode.Mode.STAMP_MODIFY
+                    Annotation.Type.CIRCLE, Annotation.Type.SQUARE, Annotation.Type.LINE -> KMPDFAnnotEditMode.Mode.SHAPE_MODIFY
+                    else -> KMPDFAnnotEditMode.Mode.NULL
+                }
+                if (mode != KMPDFAnnotEditMode.Mode.NULL) {
+                    kmpdfFactory?.kmpdfAnnotEditMode?.pdfAnnotEditMode = mode
+                }
+            }
+        }
     }
 
     fun setPageIndex(pageIndex: Int){
@@ -206,6 +335,44 @@ class ReaderViewModel(private val pdfSdkLicense: String, private val pdfSdkRsaMs
         }
     }
 
+    fun setTextBoxAttribute(attr: TextBoxAttribute) {
+
+        val kmpdfFreetextAnnotationBean = KMPDFFreetextAnnotationBean("", attr.textColor, attr.textSize, 255, "Courier", attr.isBold, attr.isItalic)
+        kmpdfFactory?.setAnnotationAttribute(kmpdfFreetextAnnotationBean)
+        kmpdfFactory?.setAnnotationEditMode(KMPDFAnnotationBean.AnnotationType.FREETEXT)
+    }
+
+    fun setSignatureAttribute(attr: SignatureAttribute) {
+
+        val kmpdfSignatureAnnotationBean = KMPDFSignatureAnnotationBean("", attr.path, KMPDFSignatureController.OnSignImageCreateListener {
+            println("onSignImageCreated")
+        })
+        kmpdfFactory?.setAnnotationAttribute(kmpdfSignatureAnnotationBean)
+        kmpdfFactory?.setAnnotationEditMode(KMPDFAnnotationBean.AnnotationType.SIGNATURE)
+    }
+
+    fun setStampAttribute(attr: StampAttribute) {
+
+        val kmpdfStampAnnotationBean = when (attr.isStandardStamp()) {
+            true -> KMPDFStampAnnotationBean("", KMPDFStampAnnotationBean.StampType.STANDARD, KMPDFStampAnnotationBean.StandardStamp(attr.resId))
+            false -> KMPDFStampAnnotationBean("", KMPDFStampAnnotationBean.StampType.TEXT, KMPDFStampAnnotationBean.TextStamp(attr.rect, attr.config))
+        }
+        kmpdfFactory?.setAnnotationAttribute(kmpdfStampAnnotationBean)
+        kmpdfFactory?.setAnnotationEditMode(KMPDFAnnotationBean.AnnotationType.STAMP)
+    }
+
+    fun setShapeAttribute(attr: ShapeAttribute) {
+
+        val kmpdfShapeAnnotationBean = when (attr.shapeType) {
+            AnnotConfig.ShapeAnnotationType.LINE -> KMPDFLineAnnotationBean("", attr.lineColor, attr.lineWidth, attr.lineAlpha)
+            AnnotConfig.ShapeAnnotationType.ARROW -> KMPDFArrowAnnotationBean("", attr.lineColor, attr.lineWidth, attr.lineAlpha)
+            AnnotConfig.ShapeAnnotationType.CIRCLE -> KMPDFCircleAnnotationBean("", attr.lineColor, attr.lineWidth, attr.lineAlpha, attr.fillColor, attr.fillAlpha)
+            AnnotConfig.ShapeAnnotationType.SQUARE -> KMPDFSquareAnnotationBean("", attr.lineColor, attr.lineWidth, attr.lineAlpha, attr.fillColor, attr.fillAlpha)
+        }
+        kmpdfFactory?.setAnnotationAttribute(kmpdfShapeAnnotationBean)
+        kmpdfFactory?.setAnnotationEditMode(kmpdfShapeAnnotationBean.type)
+    }
+
     val pdfInfoHandler = object : PdfInfoHandler {
         override fun getOpenPdfFilename(): String {
             return kmpdfFactory?.fileName ?: ""

+ 10 - 0
reader/src/main/java/com/kdanmobile/reader/Utils.kt

@@ -15,6 +15,7 @@ import android.support.v7.widget.AppCompatImageView
 import android.transition.AutoTransition
 import android.transition.TransitionManager
 import android.view.View
+import android.view.ViewGroup
 import android.widget.ImageView
 
 object Utils {
@@ -68,4 +69,13 @@ object Utils {
         constraintSet.clone(context, layoutResId)
         this.applyConstraintSet(constraintSet, duration)
     }
+
+    fun makeDropDownMeasureSpec(measureSpec: Int): Int {
+        val mode = if (measureSpec == ViewGroup.LayoutParams.WRAP_CONTENT) {
+            View.MeasureSpec.UNSPECIFIED
+        } else {
+            View.MeasureSpec.EXACTLY
+        }
+        return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec), mode)
+    }
 }

+ 5 - 0
reader/src/main/java/com/kdanmobile/reader/annotationattribute/AnnotationAttribute.kt

@@ -0,0 +1,5 @@
+package com.kdanmobile.reader.annotationattribute
+
+import android.support.annotation.IntRange
+
+open class AnnotationAttribute(open var color: Int, @IntRange(from = 0, to = 255) open var alpha: Int)

+ 16 - 0
reader/src/main/java/com/kdanmobile/reader/annotationattribute/AnnotationColor.kt

@@ -0,0 +1,16 @@
+package com.kdanmobile.reader.annotationattribute
+
+import com.kdanmobile.reader.R
+
+enum class AnnotationColor(val colorResId: Int) {
+    Color1(R.color.reader_annotation_color_1),
+    Color2(R.color.reader_annotation_color_2),
+    Color3(R.color.reader_annotation_color_3),
+    Color4(R.color.reader_annotation_color_4),
+    Color5(R.color.reader_annotation_color_5),
+    Color6(R.color.reader_annotation_color_6),
+    Color7(R.color.reader_annotation_color_7),
+    Color8(R.color.reader_annotation_color_8),
+    Color9(R.color.reader_annotation_color_9),
+    Color10(R.color.reader_annotation_color_10),
+}

+ 6 - 0
reader/src/main/java/com/kdanmobile/reader/annotationattribute/Brush.kt

@@ -0,0 +1,6 @@
+package com.kdanmobile.reader.annotationattribute
+
+enum class Brush(val maxSize: Int) {
+    Fountain(9),
+    Marker(16),
+}

+ 9 - 0
reader/src/main/java/com/kdanmobile/reader/annotationattribute/InkAttribute.kt

@@ -0,0 +1,9 @@
+package com.kdanmobile.reader.annotationattribute
+
+import android.support.annotation.IntRange
+
+data class InkAttribute(
+        override var color: Int,
+        @IntRange(from = 0, to = 255) override var alpha: Int,
+        var width: Float,
+        var brush: Brush) : AnnotationAttribute(color, alpha)

+ 146 - 0
reader/src/main/java/com/kdanmobile/reader/screen/SignatureActivity.kt

@@ -0,0 +1,146 @@
+package com.kdanmobile.reader.screen
+
+import android.support.v7.app.AppCompatActivity
+import android.os.Bundle
+import android.view.MenuItem
+import android.view.View
+import android.widget.SeekBar
+import com.kdanmobile.reader.R
+import com.kdanmobile.reader.screen.view.ColorSelectView
+import com.kdanmobile.reader.screen.view.edit.SignatureDrawView
+import kotlinx.android.synthetic.main.activity_view_signature_create.*
+import android.graphics.Bitmap
+import android.support.v4.content.ContextCompat
+import com.kdanmobile.reader.Config
+import com.kdanmobile.reader.screen.view.edit.OnSignatureAddListener
+import java.io.File
+import java.io.FileOutputStream
+import java.io.IOException
+
+class SignatureActivity : AppCompatActivity() {
+
+    companion object {
+        var signatureAddNotifyManager: OnSignatureAddListener? = null
+    }
+
+    private var onSignatureAddListener: OnSignatureAddListener? = null
+    private var noSignature = true
+        set(value) {
+            field = value
+            when (field) {
+                true -> {
+                    tv_writeHere.visibility = View.VISIBLE
+                    btn_save_signature.setTextColor(ContextCompat.getColor(baseContext, android.R.color.darker_gray))
+                    btn_save_signature.isClickable = false
+                }
+                false -> {
+                    tv_writeHere.visibility = View.INVISIBLE
+                    btn_save_signature.setTextColor(ContextCompat.getColor(baseContext, android.R.color.white))
+                    btn_save_signature.isClickable = true
+                }
+            }
+        }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_view_signature_create)
+
+        onSignatureAddListener = signatureAddNotifyManager
+        signatureAddNotifyManager = null
+
+        setupToolbar()
+
+        colorChooser_signature.onColorSelectedListener = object : ColorSelectView.OnColorSelectedListener {
+            override fun onColorSelected(color: Int) {
+                drawView.paintColor = color
+            }
+        }
+        drawView.paintColor = colorChooser_signature.getSelectedColor()
+
+        seekBar_signature.onSeekBarChangeListener = object : SeekBar.OnSeekBarChangeListener {
+            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+                drawView.strokeWidth = seekBar_signature.currentValue.toFloat()
+            }
+
+            override fun onStartTrackingTouch(seekBar: SeekBar?) {}
+
+            override fun onStopTrackingTouch(seekBar: SeekBar?) {
+                drawView.strokeWidth = seekBar_signature.currentValue.toFloat()
+            }
+        }
+        drawView.strokeWidth = seekBar_signature.currentValue.toFloat()
+
+        btn_save_signature.setOnClickListener {
+            if (!noSignature) {
+                val bitmap = drawView.getBitmap()
+
+                val argb = IntArray(bitmap.width * bitmap.height)
+                bitmap.getPixels(argb, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
+                var minX = bitmap.width
+                var maxX = 0
+                var minY = bitmap.height
+                var maxY = 0
+                var index = 0
+                for (y in 0 until bitmap.height) {
+                    for (x in 0 until bitmap.width) {
+                        val alpha = (argb[index] shr 24) and 0xff
+                        if (alpha > 10) {
+                            minX = Math.min(x, minX)
+                            maxX = Math.max(x, maxX)
+                            minY = Math.min(y, minY)
+                            maxY = Math.max(y, maxY)
+                        }
+                        index++
+                    }
+                }
+
+                val cropBitmap = Bitmap.createBitmap(bitmap, minX, minY, maxX - minX + 1, maxY - minY + 1)
+
+                val path = Config.getSignatureDirectoryPath(baseContext)
+                val dir = File(path)
+                if (!dir.exists() || !dir.isDirectory) {
+                    dir.mkdirs()
+                }
+                val filename = "$path${File.separator}Signature${System.currentTimeMillis()}.png"
+                try {
+                    val fileOutputStream = FileOutputStream(filename)
+                    cropBitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream)
+                    fileOutputStream.close()
+                } catch (e: IOException) {
+                    e.printStackTrace()
+                }
+
+                onSignatureAddListener?.onSignatureAdd(filename)
+                finish()
+            }
+        }
+
+        btn_clear_signature.setOnClickListener {
+            drawView.clear()
+        }
+
+        drawView.onSignatureDrawListener = object : SignatureDrawView.OnSignatureDrawListener {
+            override fun onClearSignature() {
+                noSignature = true
+            }
+
+            override fun onStartSignature() {
+                noSignature = false
+            }
+        }
+        noSignature = true
+    }
+
+    private fun setupToolbar() {
+        setSupportActionBar(toolbar_signature)
+        supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close)
+        supportActionBar?.setDisplayHomeAsUpEnabled(true)
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem?): Boolean {
+        when (item?.itemId) {
+            android.R.id.home -> finish()
+        }
+        return super.onOptionsItemSelected(item)
+    }
+}

+ 5 - 0
reader/src/main/java/com/kdanmobile/reader/screen/data/ShapeAttribute.kt

@@ -0,0 +1,5 @@
+package com.kdanmobile.reader.screen.data
+
+import com.kdanmobile.kmpdfkit.globaldata.AnnotConfig
+
+data class ShapeAttribute(val shapeType: AnnotConfig.ShapeAnnotationType, val lineColor: Int, val lineWidth: Float, val lineAlpha: Int, val fillColor: Int, val fillAlpha: Int)

+ 3 - 0
reader/src/main/java/com/kdanmobile/reader/screen/data/SignatureAttribute.kt

@@ -0,0 +1,3 @@
+package com.kdanmobile.reader.screen.data
+
+data class SignatureAttribute(val path: String)

+ 11 - 0
reader/src/main/java/com/kdanmobile/reader/screen/data/StampAttribute.kt

@@ -0,0 +1,11 @@
+package com.kdanmobile.reader.screen.data
+
+import android.graphics.Rect
+import com.kdanmobile.kmpdfkit.annotation.stamp.StampConfig
+import com.kdanmobile.kmpdfkit.annotation.stamp.TextStampConfig
+
+data class StampAttribute(val resId: StampConfig.STANDARD_STAMP_RES? = null, val rect: Rect? = null, val config: TextStampConfig? = null) {
+    fun isStandardStamp(): Boolean {
+        return null != resId
+    }
+}

+ 3 - 0
reader/src/main/java/com/kdanmobile/reader/screen/data/TextBoxAttribute.kt

@@ -0,0 +1,3 @@
+package com.kdanmobile.reader.screen.data
+
+data class TextBoxAttribute(val isBold: Boolean, val isItalic: Boolean, val textAlignment: Int, val textColor: Int, val textSize: Int)

+ 62 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/ColorOvalView.kt

@@ -0,0 +1,62 @@
+package com.kdanmobile.reader.screen.view
+
+import android.content.Context
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.StateListDrawable
+import android.support.constraint.ConstraintLayout
+import android.support.v4.content.ContextCompat
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import com.kdanmobile.reader.R
+import kotlinx.android.synthetic.main.view_color_oval.view.*
+
+class ColorOvalView : ConstraintLayout {
+
+    var color: Int = android.R.color.black
+        set(value) {
+            field = value
+
+            initColor(value)
+        }
+
+    constructor(context: Context) : super(context) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        initAttr(attrs)
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        initAttr(attrs)
+        initView()
+    }
+
+    private fun initAttr(attributeSet: AttributeSet) {
+        val typedArray = context.theme.obtainStyledAttributes(attributeSet, R.styleable.ColorOvalView, 0, 0)
+        color = typedArray.getColor(R.styleable.ColorOvalView_color, ContextCompat.getColor(context, android.R.color.black))
+    }
+
+    private fun initView() {
+        LayoutInflater.from(context).inflate(R.layout.view_color_oval, this)
+        initColor(color)
+    }
+
+    override fun setSelected(selected: Boolean) {
+        super.setSelected(selected)
+
+        colorOval.isSelected = selected
+        initColor(color)
+    }
+
+    private fun initColor(color: Int) {
+        colorOval?.apply {
+            val stateListDrawable = background.mutate() as StateListDrawable
+            val layerDrawable = stateListDrawable.current.mutate() as LayerDrawable
+            val gradientDrawable = layerDrawable.findDrawableByLayerId(R.id.ovalColor).mutate() as GradientDrawable
+            gradientDrawable.setColor(color)
+        }
+    }
+}

+ 91 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/ColorSelectView.kt

@@ -0,0 +1,91 @@
+package com.kdanmobile.reader.screen.view
+
+import android.content.Context
+import android.support.constraint.ConstraintLayout
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import com.kdanmobile.reader.R
+import kotlinx.android.synthetic.main.view_color_select.view.*
+
+class ColorSelectView : ConstraintLayout {
+
+    interface OnColorSelectedListener {
+        fun onColorSelected(color: Int)
+    }
+
+    var onColorSelectedListener: OnColorSelectedListener? = null
+
+    private var selectedColor: Int = android.R.color.black
+
+    constructor(context: Context) : super(context) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        initView()
+    }
+
+    private fun initView() {
+        LayoutInflater.from(context).inflate(R.layout.view_color_select, this)
+
+        colorSelect_column1.setOnClickListener(this::setColorSelect)
+        colorSelect_column2.setOnClickListener(this::setColorSelect)
+        colorSelect_column3.setOnClickListener(this::setColorSelect)
+        colorSelect_column4.setOnClickListener(this::setColorSelect)
+        colorSelect_column5.setOnClickListener(this::setColorSelect)
+        colorSelect_column6.setOnClickListener(this::setColorSelect)
+        colorSelect_column7.setOnClickListener(this::setColorSelect)
+        colorSelect_column8.setOnClickListener(this::setColorSelect)
+
+        setColorSelect(colorSelect_column1)
+    }
+
+    private fun setColorSelect(view: View) {
+        val colorOvalView = view as ColorOvalView
+
+        colorSelect_column1.isSelected = (colorOvalView == colorSelect_column1)
+        colorSelect_column2.isSelected = (colorOvalView == colorSelect_column2)
+        colorSelect_column3.isSelected = (colorOvalView == colorSelect_column3)
+        colorSelect_column4.isSelected = (colorOvalView == colorSelect_column4)
+        colorSelect_column5.isSelected = (colorOvalView == colorSelect_column5)
+        colorSelect_column6.isSelected = (colorOvalView == colorSelect_column6)
+        colorSelect_column7.isSelected = (colorOvalView == colorSelect_column7)
+        colorSelect_column8.isSelected = (colorOvalView == colorSelect_column8)
+
+        onColorSelectedListener?.onColorSelected(colorOvalView.color)
+        selectedColor = colorOvalView.color
+    }
+
+    fun getSelectedColor() : Int {
+        return selectedColor
+    }
+
+    fun setSelectedColor(color: Int) : Boolean {
+        val columnArray = arrayOf(colorSelect_column1, colorSelect_column2, colorSelect_column3, colorSelect_column4, colorSelect_column5, colorSelect_column6, colorSelect_column7, colorSelect_column8)
+        for (column in columnArray) {
+            if (column.color == color) {
+                setColorSelect(column)
+                return true
+            }
+        }
+        return false
+    }
+
+    fun getColor(index: Int) : Int {
+        return when (index) {
+            0 -> colorSelect_column1.color
+            1 -> colorSelect_column2.color
+            2 -> colorSelect_column3.color
+            3 -> colorSelect_column4.color
+            4 -> colorSelect_column5.color
+            5 -> colorSelect_column6.color
+            6 -> colorSelect_column7.color
+            else -> colorSelect_column8.color
+        }
+    }
+}

+ 161 - 31
reader/src/main/java/com/kdanmobile/reader/screen/view/ViewerEditView.kt

@@ -1,43 +1,146 @@
 package com.kdanmobile.reader.screen.view
 
+import android.animation.*
 import android.content.Context
+import android.graphics.Color
 import android.support.constraint.ConstraintLayout
 import android.support.v4.content.ContextCompat
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.view.View
+import android.view.View.OnClickListener
+import android.view.animation.AccelerateDecelerateInterpolator
 import android.widget.ImageButton
 import com.kdanmobile.reader.R
-import com.kdanmobile.reader.screen.view.edit.TextBoxTabView
+import com.kdanmobile.reader.screen.view.edit.*
 import kotlinx.android.synthetic.main.view_viewer_edit.view.*
-
-class ViewerEditView : ConstraintLayout {
+import kotlinx.android.synthetic.main.view_viewer_edit_tab.view.*
+
+class ViewerEditView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+): ConstraintLayout(context, attrs, defStyleAttr) {
+
+    private companion object {
+        const val COLOR_DISMISS = Color.TRANSPARENT
+        val COLOR_SHOW = Color.parseColor("#42000000")
+        const val DEFAULT_DURATION_ANIMATION = 300L
+    }
 
     private enum class ViewerEditTabType {
         NONE, TEXT_BOX, SIGNATURE, STAMP, SHAPE, FORM
     }
 
+    var onShowListener: Runnable? = null
+    var onDismissListener: Runnable? = null
+    var animationDuration = DEFAULT_DURATION_ANIMATION
+
+    private var mediaBoxAnimator: ObjectAnimator? = null
+    private var maskAnimator: ValueAnimator? = null
     private var tabType = ViewerEditTabType.NONE
     private var selected: ImageButton? = null
+    var onViewerEditTabAddButtonClickListener: OnViewerEditTabAddButtonClickListener? = null
+
+    init {
+        LayoutInflater.from(context).inflate(R.layout.view_viewer_edit, this)
+        visibility = View.INVISIBLE
+        setupTabView()
 
-//    private val selectedColor = ContextCompat.getColor(context, R.color.reader_viewer_edit_tab_color_selected)
+        /** Set empty listener to prevent dismissing by clicking box **/
+        view_viewEdit_content.setOnClickListener {}
 
-    constructor(context: Context) : super(context) {
-        initView()
+        /** Move media box view to bottom of parent(hide it) **/
+        val size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
+        val v = view_viewEdit_content
+        v.measure(size, size)
+        v.y = v.top + v.measuredHeight.toFloat()
     }
 
-    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
-        initView()
+    fun show() {
+        visibility = View.VISIBLE
+        setOnClickListener { dismiss() }
+        post {
+            showMaskWithAnimation()
+            showMediaBoxWithAnimation()
+        }
+        onShowListener?.run()
     }
 
-    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
-        initView()
+    fun dismiss() {
+        setOnClickListener(null)
+        hideMaskWithAnimation()
+        hideMediaBoxWithAnimation()
+        onDismissListener?.run()
     }
 
-    private fun initView() {
-        LayoutInflater.from(context).inflate(R.layout.view_viewer_edit, this)
+    private fun showMaskWithAnimation() {
+        maskAnimator?.cancel()
+        val from = maskAnimator?.animatedValue ?: COLOR_DISMISS
+        val to = COLOR_SHOW
+        maskAnimator = ValueAnimator.ofObject(ArgbEvaluator(), from, to).apply {
+            duration = DEFAULT_DURATION_ANIMATION
+            interpolator = AccelerateDecelerateInterpolator()
+            addUpdateListener {
+                setBackgroundColor(it.animatedValue as Int)
+            }
+            start()
+        }
+    }
 
-        setupTabView()
+    private fun hideMaskWithAnimation() {
+        maskAnimator?.cancel()
+        val from = maskAnimator?.animatedValue ?: COLOR_SHOW
+        val to = COLOR_DISMISS
+        maskAnimator = ValueAnimator.ofObject(ArgbEvaluator(), from, to).apply {
+            duration = DEFAULT_DURATION_ANIMATION
+            interpolator = AccelerateDecelerateInterpolator()
+            addUpdateListener {
+                setBackgroundColor(it.animatedValue as Int)
+            }
+            start()
+        }
+    }
+
+    private fun showMediaBoxWithAnimation() {
+        mediaBoxAnimator?.cancel()
+        val v = view_viewEdit_content
+        v.visibility = View.VISIBLE
+        val from = v.y
+        val to = v.top.toFloat()
+        val holder = PropertyValuesHolder.ofFloat("Y", from, to)
+        mediaBoxAnimator = ObjectAnimator.ofPropertyValuesHolder(v, holder).apply {
+            interpolator = AccelerateDecelerateInterpolator()
+            duration = animationDuration
+            addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator?) {
+                    super.onAnimationEnd(animation)
+                    removeListener(this)
+                }
+            })
+            start()
+        }
+    }
+
+    private fun hideMediaBoxWithAnimation() {
+        mediaBoxAnimator?.cancel()
+        val v = view_viewEdit_content
+        val from = v.y
+        val to = v.top.toFloat() + v.height
+        val holder = PropertyValuesHolder.ofFloat("Y", from, to)
+        mediaBoxAnimator = ObjectAnimator.ofPropertyValuesHolder(v, holder).apply {
+            interpolator = AccelerateDecelerateInterpolator()
+            duration = animationDuration
+            addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator?) {
+                    super.onAnimationEnd(animation)
+                    removeListener(this)
+                    v.visibility = View.INVISIBLE
+                    this@ViewerEditView.visibility = View.GONE
+                }
+            })
+            start()
+        }
     }
 
     private fun setupTabView() {
@@ -46,20 +149,22 @@ class ViewerEditView : ConstraintLayout {
         viewerEdit_tab.onClickListenerTabStamp = OnClickListener(this::setupStampView)
         viewerEdit_tab.onClickListenerTabShape = OnClickListener(this::setupShapeView)
         viewerEdit_tab.onClickListenerTabForm = OnClickListener(this::setupFormView)
+
+        setupTextBoxView(viewerEdit_tab_textBox)
     }
 
     private fun getButtonDrawable(tabType: ViewerEditTabType, isSelected: Boolean): Int {
-        when (tabType) {
+        return when (tabType) {
             ViewerEditTabType.NONE, ViewerEditTabType.TEXT_BOX ->
-                return if (isSelected) R.drawable.ic_textbox_selected else R.drawable.ic_textbox
+                if (isSelected) R.drawable.ic_textbox_selected else R.drawable.ic_textbox
             ViewerEditTabType.SIGNATURE ->
-                return if (isSelected) R.drawable.ic_signature_selected else R.drawable.ic_signature
+                if (isSelected) R.drawable.ic_signature_selected else R.drawable.ic_signature
             ViewerEditTabType.STAMP ->
-                return if (isSelected) R.drawable.ic_stamp_selected else R.drawable.ic_stamp
+                if (isSelected) R.drawable.ic_stamp_selected else R.drawable.ic_stamp
             ViewerEditTabType.SHAPE ->
-                return if (isSelected) R.drawable.ic_shape_selected else R.drawable.ic_shape
+                if (isSelected) R.drawable.ic_shape_selected else R.drawable.ic_shape
             ViewerEditTabType.FORM ->
-                return if (isSelected) R.drawable.ic_form_selected else R.drawable.ic_form
+                if (isSelected) R.drawable.ic_form_selected else R.drawable.ic_form
         }
     }
 
@@ -68,12 +173,8 @@ class ViewerEditView : ConstraintLayout {
         if (this.tabType != tabType) {
             selected?.setImageDrawable(ContextCompat.getDrawable(context, getButtonDrawable(this.tabType, false)))
             this.tabType = tabType
-//            selected?.clearColorFilter()
             selected = button
             button.setImageDrawable(ContextCompat.getDrawable(context, getButtonDrawable(this.tabType, true)))
-//            button.drawable.setColorFilter(selectedColor, PorterDuff.Mode.SRC_IN)
-//            button.setColorFilter(selectedColor, PorterDuff.Mode.SRC_IN)
-//            button.setColorFilter(Color.BLACK, PorterDuff.Mode.SCREEN)
 
             viewEdit_layout_tab_content.removeAllViews()
             changed = true
@@ -82,44 +183,73 @@ class ViewerEditView : ConstraintLayout {
     }
 
     private fun setupTextBoxView(view: View) {
-        //TODO implements tab view and add to viewEdit_layout_tab_content
         val changed = setupContent(view as ImageButton, ViewerEditTabType.TEXT_BOX)
         if (changed) {
             val textBoxTabView = TextBoxTabView(context)
             val params = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT)
             viewEdit_layout_tab_content.addView(textBoxTabView, params)
+            textBoxTabView.onTitleButtonClickListener = object: TitleButton.OnTitleButtonClickListener {
+                override fun onTitleButtonClick() {
+                    onViewerEditTabAddButtonClickListener?.onTabTextBoxAddButtonClick(textBoxTabView)
+                }
+            }
         }
     }
 
     private fun setupSignatureView(view: View) {
-        //TODO implements tab view and add to viewEdit_layout_tab_content
         val changed = setupContent(view as ImageButton, ViewerEditTabType.SIGNATURE)
         if (changed) {
-            LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_tab, viewEdit_layout_tab_content)
+            val signatureTabView = SignatureTabView(context)
+            val params = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT)
+            viewEdit_layout_tab_content.addView(signatureTabView, params)
+            signatureTabView.onTitleButtonClickListener = object: TitleButton.OnTitleButtonClickListener {
+                override fun onTitleButtonClick() {
+                    onViewerEditTabAddButtonClickListener?.onTabSignatureAddButtonClick(signatureTabView)
+                }
+            }
         }
     }
 
     private fun setupStampView(view: View) {
-        //TODO implements tab view and add to viewEdit_layout_tab_content
         val changed = setupContent(view as ImageButton, ViewerEditTabType.STAMP)
         if (changed) {
-            LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_tab, viewEdit_layout_tab_content)
+            val stampTabView = StampTabView(context)
+            val params = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT)
+            viewEdit_layout_tab_content.addView(stampTabView, params)
+            stampTabView.onTitleButtonClickListener = object: TitleButton.OnTitleButtonClickListener {
+                override fun onTitleButtonClick() {
+                    onViewerEditTabAddButtonClickListener?.onTabStampAddButtonClick(stampTabView)
+                }
+            }
         }
     }
 
     private fun setupShapeView(view: View) {
-        //TODO implements tab view and add to viewEdit_layout_tab_content
         val changed = setupContent(view as ImageButton, ViewerEditTabType.SHAPE)
         if (changed) {
-            LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_tab, viewEdit_layout_tab_content)
+            val shapeTabView = ShapeTabView(context)
+            val params = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT)
+            viewEdit_layout_tab_content.addView(shapeTabView, params)
+            shapeTabView.onTitleButtonClickListener = object: TitleButton.OnTitleButtonClickListener {
+                override fun onTitleButtonClick() {
+                    onViewerEditTabAddButtonClickListener?.onTabShapeAddButtonClick(shapeTabView)
+                }
+            }
         }
     }
 
     private fun setupFormView(view: View) {
-//TODO implements tab view and add to viewEdit_layout_tab_content
+        //TODO implements tab view and add to viewEdit_layout_tab_content
         val changed = setupContent(view as ImageButton, ViewerEditTabType.FORM)
         if (changed) {
             LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_tab, viewEdit_layout_tab_content)
         }
     }
+
+    interface OnViewerEditTabAddButtonClickListener {
+        fun onTabTextBoxAddButtonClick(textBoxTabView: TextBoxTabView)
+        fun onTabSignatureAddButtonClick(signatureTabView: SignatureTabView)
+        fun onTabStampAddButtonClick(stampTabView: StampTabView)
+        fun onTabShapeAddButtonClick(shapeTabView: ShapeTabView)
+    }
 }

+ 66 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/CustomStampAdapter.kt

@@ -0,0 +1,66 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import android.content.Context
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import com.kdanmobile.reader.R
+import kotlinx.android.synthetic.main.view_viewer_edit_item_stamp_custom_viewholder.view.*
+
+class CustomStampAdapter(private val context: Context, private val stampTypes: Array<CustomStampType>): RecyclerView.Adapter<CustomStampViewHolder>() {
+
+    private var selectedIndex = 0
+    private var onStampClickListener: OnStampClickListener? = null
+
+    override fun onBindViewHolder(holder: CustomStampViewHolder, position: Int) {
+        val stampType = stampTypes[position]
+//            val stampTextView = holder.kmpdfStampTextView
+//            stampTextView.content = "a"
+//            stampTextView.shape = stampType.shape
+//            stampTextView.bgColor = stampType.backgroundColor
+//            stampTextView.textColor = stampType.textColor
+//            stampTextView.lineColor = stampType.lineColor
+//            stampTextView.lineWidth = 2f
+        holder.view.setOnClickListener {
+            if (position != selectedIndex) {
+                val oldSelectedIndex = selectedIndex
+                selectedIndex = position
+                notifyItemChanged(selectedIndex)
+                notifyItemChanged(oldSelectedIndex)
+                onStampClickListener?.onStampClick(selectedIndex)
+            }
+        }
+        holder.ivStamp.setImageResource(stampType.resourceId)
+        holder.ivSelectedBorder.visibility = when (position == selectedIndex) {
+            true -> View.VISIBLE
+            false -> View.INVISIBLE
+        }
+        holder.ivSelectedArrow.visibility = when (position == selectedIndex) {
+            true -> View.VISIBLE
+            false -> View.INVISIBLE
+        }
+    }
+
+    override fun getItemCount(): Int {
+        return stampTypes.size
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomStampViewHolder {
+        val view = LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_item_stamp_custom_viewholder, null)
+//            val view = ImageView(context)
+        return CustomStampViewHolder(view)
+    }
+
+    fun setOnStampClickListener(onStampClickListener: OnStampClickListener?) {
+        this.onStampClickListener = onStampClickListener
+    }
+}
+
+class CustomStampViewHolder(val view: View): RecyclerView.ViewHolder(view) {
+//        val kmpdfStampTextView: KMPDFStampTextView = view.findViewById(R.id.kmpdfStampTextView)
+    val ivStamp: ImageView = view.iv_stamp
+    val ivSelectedBorder: View = view.iv_selectedBorder
+    val ivSelectedArrow: ImageView = view.iv_selectedArrow
+}

+ 19 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/CustomStampType.kt

@@ -0,0 +1,19 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import com.kdanmobile.kmpdfkit.annotation.stamp.StampConfig
+import com.kdanmobile.kmpdfkit.annotation.stamp.view.KMPDFStampTextView
+import com.kdanmobile.reader.R
+
+enum class CustomStampType(val shape: KMPDFStampTextView.Shape, val backgroundColor: Int, val textColor: Int, val lineColor: Int, val resourceId: Int) {
+//        GREEN_LEFT_ARROW    (Shape.LEFT_RECT,   Color.parseColor("#ff0000"), StandardColor.text_green,   StandardColor.line_green),
+    GREEN_LEFT_ARROW    (KMPDFStampTextView.Shape.LEFT_RECT,   StampConfig.StandardColor.bg_green, StampConfig.StandardColor.text_green,   StampConfig.StandardColor.line_green,   R.drawable.stamp_green_left_arrow),
+    GREEN_RECTANGLE     (KMPDFStampTextView.Shape.RECT,        StampConfig.StandardColor.bg_green, StampConfig.StandardColor.text_green,   StampConfig.StandardColor.line_green,   R.drawable.stamp_green_rect),
+    GREEN_RIGHT_ARROW   (KMPDFStampTextView.Shape.RIGHT_RECT,  StampConfig.StandardColor.bg_green, StampConfig.StandardColor.text_green,   StampConfig.StandardColor.line_green,   R.drawable.stamp_green_right_arrow),
+    BLUE_LEFT_ARROW     (KMPDFStampTextView.Shape.LEFT_RECT,   StampConfig.StandardColor.bg_blue,  StampConfig.StandardColor.text_blue,    StampConfig.StandardColor.line_blue,    R.drawable.stamp_blue_left_arrow),
+    BLUE_RECTANGLE      (KMPDFStampTextView.Shape.RECT,        StampConfig.StandardColor.bg_blue,  StampConfig.StandardColor.text_blue,    StampConfig.StandardColor.line_blue,    R.drawable.stamp_blue_rect),
+    BLUE_RIGHT_ARROW    (KMPDFStampTextView.Shape.RIGHT_RECT,  StampConfig.StandardColor.bg_blue,  StampConfig.StandardColor.text_blue,    StampConfig.StandardColor.line_blue,    R.drawable.stamp_blue_right_arrow),
+    RED_LEFT_ARROW      (KMPDFStampTextView.Shape.LEFT_RECT,   StampConfig.StandardColor.bg_red,   StampConfig.StandardColor.text_red,     StampConfig.StandardColor.line_red,     R.drawable.stamp_red_left_arrow),
+    RED_RECTANGLE       (KMPDFStampTextView.Shape.RECT,        StampConfig.StandardColor.bg_red,   StampConfig.StandardColor.text_red,     StampConfig.StandardColor.line_red,     R.drawable.stamp_red_rect),
+    RED_RIGHT_ARROW     (KMPDFStampTextView.Shape.RIGHT_RECT,  StampConfig.StandardColor.bg_red,   StampConfig.StandardColor.text_red,     StampConfig.StandardColor.line_red,     R.drawable.stamp_red_right_arrow),
+//        NO_BORDER           (Shape.RECT,        StandardColor.bg_white, StandardColor.text_black,   StandardColor.line_white)
+}

+ 5 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/OnSignatureAddListener.kt

@@ -0,0 +1,5 @@
+package com.kdanmobile.reader.screen.view.edit
+
+interface OnSignatureAddListener {
+    fun onSignatureAdd(path: String)
+}

+ 5 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/OnSignatureClickListener.kt

@@ -0,0 +1,5 @@
+package com.kdanmobile.reader.screen.view.edit
+
+interface OnSignatureClickListener {
+    fun onSignatureClick(path: String)
+}

+ 5 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/OnSignatureRemoveListener.kt

@@ -0,0 +1,5 @@
+package com.kdanmobile.reader.screen.view.edit
+
+interface OnSignatureRemoveListener {
+    fun onSignatureRemove(path: String)
+}

+ 5 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/OnStampClickListener.kt

@@ -0,0 +1,5 @@
+package com.kdanmobile.reader.screen.view.edit
+
+interface OnStampClickListener {
+    fun onStampClick(index: Int)
+}

+ 138 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/ShapeTabView.kt

@@ -0,0 +1,138 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.PorterDuff
+import android.support.constraint.ConstraintLayout
+import android.support.v4.view.PagerAdapter
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.kdanmobile.kmpdfkit.globaldata.AnnotConfig
+import com.kdanmobile.reader.R
+import com.kdanmobile.reader.screen.data.ShapeAttribute
+import kotlinx.android.synthetic.main.view_viewer_edit_item_shape_border.view.*
+import kotlinx.android.synthetic.main.view_viewer_edit_item_shape_fill.view.*
+import kotlinx.android.synthetic.main.view_viewer_edit_tab_shape.view.*
+
+class ShapeTabView : ConstraintLayout {
+
+    var onTitleButtonClickListener: TitleButton.OnTitleButtonClickListener? = null
+        set(value) {
+            field = value
+            titleButton_shape?.onTitleButtonClickListener = field
+        }
+
+    var shapeType = AnnotConfig.ShapeAnnotationType.CIRCLE
+        set(value) {
+            field = value
+
+            btn_circle?.clearColorFilter()
+            btn_square?.clearColorFilter()
+            btn_line?.clearColorFilter()
+            btn_arrow?.clearColorFilter()
+            when (shapeType) {
+                AnnotConfig.ShapeAnnotationType.CIRCLE -> {
+                    btn_circle?.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)
+                }
+                AnnotConfig.ShapeAnnotationType.SQUARE -> {
+                    btn_square?.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)
+                }
+                AnnotConfig.ShapeAnnotationType.LINE -> {
+                    btn_line?.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)
+                }
+                AnnotConfig.ShapeAnnotationType.ARROW -> {
+                    btn_arrow?.setColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN)
+                }
+            }
+        }
+
+    constructor(context: Context) : super(context) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        initView()
+    }
+
+    private fun initView() {
+        LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_tab_shape, this)
+
+        btn_circle.setOnClickListener { _ ->
+            shapeType = AnnotConfig.ShapeAnnotationType.CIRCLE
+        }
+
+        btn_square.setOnClickListener { _ ->
+            shapeType = AnnotConfig.ShapeAnnotationType.SQUARE
+        }
+
+        btn_line.setOnClickListener { _ ->
+            shapeType = AnnotConfig.ShapeAnnotationType.LINE
+        }
+
+        btn_arrow.setOnClickListener { _ ->
+            shapeType = AnnotConfig.ShapeAnnotationType.ARROW
+        }
+        shapeType = AnnotConfig.ShapeAnnotationType.CIRCLE
+
+        viewPager.adapter = ViewPagerAdapter(context, arrayListOf("Border", "Fill"))
+        tabLayout.setupWithViewPager(viewPager)
+    }
+
+    fun getShapeAttribute() : ShapeAttribute {
+        val lineColor = colorChooser_shapeBorder.getSelectedColor()
+        val lineWidth = seekBar_shapeBorder_thickness.currentValue.toFloat()
+        val lineAlpha = seekBar_shapeBorder_opacity.currentValue
+        val fillColor = colorChooser_shapeFill.getSelectedColor()
+        val fillAlpha = seekBar_shapeFill_opacity.currentValue
+        return ShapeAttribute(shapeType, lineColor, lineWidth, lineAlpha, fillColor, fillAlpha)
+    }
+
+    private inner class ViewPagerAdapter(context: Context, private val tabs: ArrayList<String>) : PagerAdapter() {
+        private val layoutInflater = LayoutInflater.from(context)
+
+        override fun instantiateItem(container: ViewGroup, position: Int): Any {
+            val resourceId = when (position == 0) {
+                true -> R.layout.view_viewer_edit_item_shape_border
+                false -> R.layout.view_viewer_edit_item_shape_fill
+            }
+            val view = layoutInflater.inflate(resourceId, container, false)
+            if (position == 0) {
+                view.seekBar_shapeBorder_opacity.formatter = object : TextSeekBar.SeekBarFormatter {
+                    override fun format(value: Int): String {
+                        return Math.round(value / 2.55).toInt().toString()
+                    }
+                }
+            } else {
+                view.seekBar_shapeFill_opacity.formatter = object : TextSeekBar.SeekBarFormatter {
+                    override fun format(value: Int): String {
+                        return Math.round(value / 2.55).toInt().toString()
+                    }
+                }
+            }
+            container.addView(view)
+            return view
+        }
+
+        override fun destroyItem(container: ViewGroup, position: Int, obj: Any) {
+            container.removeView(obj as View)
+        }
+
+        override fun getCount(): Int {
+            return tabs.size
+        }
+
+        override fun isViewFromObject(view: View, obj: Any): Boolean {
+            return view == obj
+        }
+
+        override fun getPageTitle(position: Int): CharSequence? {
+            return tabs[position]
+        }
+    }
+}

+ 127 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/SignatureAdapter.kt

@@ -0,0 +1,127 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.Matrix
+import android.support.v4.util.LruCache
+import android.support.v7.widget.RecyclerView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import com.kdanmobile.reader.R
+import io.reactivex.Observable
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.Disposable
+import io.reactivex.schedulers.Schedulers
+import kotlinx.android.synthetic.main.view_viewer_edit_item_signature.view.*
+
+class SignatureAdapter(private val context: Context, private var signaturePaths: ArrayList<String>): RecyclerView.Adapter<SignatureViewHolder>() {
+
+    companion object {
+        val maxMemory = Runtime.getRuntime().maxMemory() / 16
+
+        val mMemoryCache: LruCache<String, Bitmap> = object: LruCache<String, Bitmap>(maxMemory.toInt()) {
+            override fun sizeOf(key: String, value: Bitmap): Int {
+                return value.byteCount
+            }
+        }
+    }
+
+    private var onSignatureClickListener: OnSignatureClickListener? = null
+    private var onSignatureRemoveListener: OnSignatureRemoveListener? = null
+
+    override fun onBindViewHolder(holder: SignatureViewHolder, position: Int) {
+        holder.filename = signaturePaths[position]
+        holder.loadThumbSync()
+        holder.ivSignature.setOnClickListener {
+            onSignatureClickListener?.onSignatureClick(holder.filename)
+        }
+        holder.btnClose.setOnClickListener {
+            onSignatureRemoveListener?.onSignatureRemove(signaturePaths[holder.adapterPosition])
+        }
+    }
+
+    override fun getItemCount(): Int {
+        return signaturePaths.size
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SignatureViewHolder {
+        val view = LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_item_signature, null)
+        return SignatureViewHolder(view)
+    }
+
+    override fun onViewRecycled(holder: SignatureViewHolder) {
+        super.onViewRecycled(holder)
+        holder.stopLoadThumbSync()
+    }
+
+    fun setOnSignatureClickListener(onSignatureClickListener: OnSignatureClickListener?) {
+        this.onSignatureClickListener = onSignatureClickListener
+    }
+
+    fun setOnSignatureRemoveListener(onSignatureRemoveListener: OnSignatureRemoveListener?) {
+        this.onSignatureRemoveListener = onSignatureRemoveListener
+    }
+}
+
+class SignatureViewHolder(view: View): RecyclerView.ViewHolder(view) {
+    val ivSignature: ImageView = view.iv_signature
+    val btnClose: View = view.btn_close
+    var filename: String = ""
+    private var disposable: Disposable? = null
+
+    fun loadThumbSync() {
+        stopLoadThumbSync()
+
+        disposable = Observable.create<Bitmap> { emitter ->
+            val bitmap: Bitmap
+            val value = SignatureAdapter.mMemoryCache[filename]
+            if (null != value) {
+                bitmap = value
+            } else {
+                bitmap = BitmapFactory.decodeFile(filename)
+                SignatureAdapter.mMemoryCache.put(filename, bitmap)
+            }
+            emitter.onNext(bitmap)
+            emitter.onComplete()
+        }
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe({
+                    ivSignature.setImageBitmap(it)
+                    ivSignature.invalidate()
+                    setScaleTypeFixLeft(ivSignature)
+                }, {
+                    it.printStackTrace()
+                })
+    }
+
+    private fun setScaleTypeFixLeft(imageView: ImageView) {
+
+        val drawableWidth = imageView.drawable.intrinsicWidth
+        val drawableHeight = imageView.drawable.intrinsicHeight
+        if (drawableWidth != 0 && drawableHeight != 0) {
+            val matrix = Matrix()
+            val viewWidth = imageView.width - imageView.paddingLeft - imageView.paddingRight
+            val viewHeight = imageView.height - imageView.paddingTop - imageView.paddingBottom
+            val scaleRatioX = viewWidth.toFloat() / drawableWidth
+            val scaleRatioY = viewHeight.toFloat() / drawableHeight
+            val scaleFactor = Math.min(scaleRatioX, scaleRatioY)
+            matrix.setScale(scaleFactor, scaleFactor)
+            matrix.postTranslate(0f, (viewHeight - drawableHeight * scaleFactor) * 0.5f)
+            imageView.imageMatrix = matrix
+        }
+    }
+
+    internal fun stopLoadThumbSync() {
+        if (null != disposable && !disposable!!.isDisposed) {
+            disposable?.dispose()
+            disposable = null
+            ivSignature.setImageBitmap(null)
+            ivSignature.invalidate()
+        }
+    }
+}

+ 103 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/SignatureDrawView.kt

@@ -0,0 +1,103 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.Path
+import android.util.AttributeSet
+import android.view.View
+import android.graphics.Paint.Cap
+import android.support.v4.content.ContextCompat
+import android.view.MotionEvent
+
+
+
+class SignatureDrawView : View {
+
+    private lateinit var paint: Paint
+    private var currentPath: Path? = null
+    private val datas = ArrayList<SignaturePathData>()
+    var onSignatureDrawListener: OnSignatureDrawListener? = null
+
+    var paintColor: Int = android.R.color.black
+    var strokeWidth: Float = 10f
+
+    constructor(context: Context) : super(context) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        initView()
+    }
+
+    private fun initView() {
+        paint = Paint()
+        paint.isAntiAlias = true
+        paint.style = Paint.Style.STROKE
+        paint.strokeCap = Cap.ROUND
+        paint.color = ContextCompat.getColor(context, paintColor)
+        paint.strokeWidth = strokeWidth
+    }
+
+    fun clear() {
+        datas.clear()
+        invalidate()
+        onSignatureDrawListener?.onClearSignature()
+    }
+
+    fun getBitmap(): Bitmap {
+        val bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888)
+        draw(Canvas(bitmap))
+        return bitmap
+    }
+
+    override fun onDraw(canvas: Canvas?) {
+        super.onDraw(canvas)
+
+        for (data in datas) {
+            paint.color = data.color
+            paint.strokeWidth = data.strokeWidth
+            canvas?.drawPath(data.path, paint)
+        }
+    }
+
+    override fun onTouchEvent(event: MotionEvent?): Boolean {
+        if (null != event) {
+            val actionIndex = event.actionIndex
+            val pointerId = event.getPointerId(actionIndex)
+            if (pointerId == 0) {
+                val x = event.getX(actionIndex)
+                val y = event.getY(actionIndex)
+                when (event.action) {
+                    MotionEvent.ACTION_DOWN -> {
+                        currentPath = Path()
+                        datas.add(SignaturePathData(currentPath!!, paintColor, strokeWidth))
+                        currentPath?.moveTo(x, y)
+                        onSignatureDrawListener?.onStartSignature()
+                    }
+                    MotionEvent.ACTION_MOVE -> {
+                        currentPath?.lineTo(x, y)
+                    }
+                    MotionEvent.ACTION_UP -> {
+                    }
+                }
+                invalidate()
+            }
+        }
+
+        return true
+    }
+
+    interface OnSignatureDrawListener {
+        fun onClearSignature()
+
+        fun onStartSignature()
+    }
+
+    data class SignaturePathData(val path: Path, val color: Int, val strokeWidth: Float)
+}

+ 18 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/SignatureRecyclerViewItemDecoration.kt

@@ -0,0 +1,18 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import android.graphics.Rect
+import android.view.View
+import android.support.v7.widget.RecyclerView
+
+
+class SignatureRecyclerViewItemDecoration(private val space: Int, private val additionalPadding: Int) : RecyclerView.ItemDecoration() {
+
+    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+        val position = parent.getChildAdapterPosition(view)
+        val childCount = parent.adapter?.itemCount ?: 0
+        outRect.bottom = when (position == childCount - 1) {
+            true -> space + additionalPadding
+            false -> space
+        }
+    }
+}

+ 101 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/SignatureTabView.kt

@@ -0,0 +1,101 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import android.content.Context
+import android.content.Intent
+import android.support.constraint.ConstraintLayout
+import android.support.v7.widget.GridLayoutManager
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import com.kdanmobile.reader.Config
+import com.kdanmobile.reader.R
+import com.kdanmobile.reader.screen.SignatureActivity
+import com.kdanmobile.reader.screen.data.SignatureAttribute
+import kotlinx.android.synthetic.main.view_viewer_edit_tab_signature.view.*
+import java.io.File
+import java.util.*
+import android.support.v7.widget.DividerItemDecoration
+import com.kdanmobile.reader.utils.DensityUtil
+
+
+class SignatureTabView : ConstraintLayout {
+
+    var onTitleButtonClickListener: TitleButton.OnTitleButtonClickListener? = null
+
+    private lateinit var signaturePaths: ArrayList<String>
+    private lateinit var selectedSignaturePath: String
+
+    constructor(context: Context) : super(context) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        initView()
+    }
+
+    private fun initView() {
+        LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_tab_signature, this)
+
+        updateSignaturePaths()
+        updateSignatureMessage()
+
+        recyclerView_signature.layoutManager = GridLayoutManager(context, 1)
+        val adapter = SignatureAdapter(context, signaturePaths)
+        adapter.setOnSignatureClickListener(object : OnSignatureClickListener {
+            override fun onSignatureClick(path: String) {
+                selectedSignaturePath = path
+                onTitleButtonClickListener?.onTitleButtonClick()
+            }
+        })
+        adapter.setOnSignatureRemoveListener(object : OnSignatureRemoveListener {
+            override fun onSignatureRemove(path: String) {
+                val position = signaturePaths.indexOf(path)
+                signaturePaths.removeAt(position)
+                adapter.notifyItemRemoved(position)
+                File(path).delete()
+                updateSignatureMessage()
+            }
+        })
+        btn_add_signature.setOnClickListener {
+            SignatureActivity.signatureAddNotifyManager = object : OnSignatureAddListener {
+                override fun onSignatureAdd(path: String) {
+                    signaturePaths.add(path)
+                    adapter.notifyDataSetChanged()
+                    recyclerView_signature.smoothScrollToPosition(signaturePaths.size - 1)
+                    updateSignatureMessage()
+                }
+            }
+            context.startActivity(Intent(context, SignatureActivity::class.java))
+        }
+        recyclerView_signature.adapter = adapter
+        recyclerView_signature.setHasFixedSize(true)
+
+        recyclerView_signature.addItemDecoration(SignatureRecyclerViewItemDecoration(DensityUtil.dp2px(context, 8f), DensityUtil.dp2px(context, 88f)))
+    }
+
+    fun getSignatureAttribute() : SignatureAttribute {
+        return SignatureAttribute(selectedSignaturePath)
+    }
+
+    private fun updateSignaturePaths() {
+        val path = Config.getSignatureDirectoryPath(context)
+        val array = File(path).list { _, name -> name.toLowerCase(Locale.US).endsWith(".png") }
+        signaturePaths = when (null != array) {
+            true -> array.toCollection(ArrayList())
+            false -> arrayListOf()
+        }
+        signaturePaths.sort()
+        for (i in 0 until signaturePaths.size) {
+            signaturePaths[i] = "$path${File.separator}${signaturePaths[i]}"
+        }
+    }
+
+    private fun updateSignatureMessage() {
+        tv_noSignature.visibility = if (signaturePaths.size == 0) View.VISIBLE else View.INVISIBLE
+        recyclerView_signature.visibility = if (signaturePaths.size == 0) View.INVISIBLE else View.VISIBLE
+    }
+}

+ 147 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/StampTabView.kt

@@ -0,0 +1,147 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import android.content.Context
+import android.graphics.Rect
+import android.support.constraint.ConstraintLayout
+import android.support.v4.view.PagerAdapter
+import android.support.v7.widget.GridLayoutManager
+import android.support.v7.widget.LinearLayoutManager
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import com.kdanmobile.kmpdfkit.annotation.stamp.StampConfig.STANDARD_STAMP_RES
+import com.kdanmobile.kmpdfkit.annotation.stamp.TextStampConfig
+import com.kdanmobile.kmpdfkit.annotation.stamp.view.KMPDFStampTextView
+import com.kdanmobile.reader.R
+import com.kdanmobile.reader.screen.data.StampAttribute
+import kotlinx.android.synthetic.main.view_viewer_edit_item_stamp_custom.view.*
+import kotlinx.android.synthetic.main.view_viewer_edit_item_stamp_standard.view.*
+import kotlinx.android.synthetic.main.view_viewer_edit_tab_stamp.view.*
+import java.text.SimpleDateFormat
+import java.util.*
+
+class StampTabView : ConstraintLayout {
+
+    private var selectedCustomStampIndex = 0
+    private var selectedStandardStampIndex = 0
+
+    var onTitleButtonClickListener: TitleButton.OnTitleButtonClickListener? = null
+        set(value) {
+            field = value
+            titleButton_stamp?.onTitleButtonClickListener = field
+        }
+
+    constructor(context: Context) : super(context) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+        initView()
+    }
+
+    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {
+        initView()
+    }
+
+    private fun initView() {
+        LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_tab_stamp, this)
+
+        viewPager.adapter = StampViewPagerAdapter(arrayListOf("Custom", "Standard"))
+        tabLayout.setupWithViewPager(viewPager)
+    }
+
+    fun getStampAttribute() : StampAttribute {
+        return when (viewPager.currentItem == 0) {
+            true -> {
+                val stampType = CustomStampType.values()[selectedCustomStampIndex]
+                //  not work?
+                val rect = Rect(0, 0, 100, 100)
+//                val timeText = "${cb_date.text}${if (cb_date.text.isNotEmpty()) " " else ""}${cb_time.text}"
+                val timeText: String
+                //  not work?
+                val timeType = if (cb_time.isChecked) {
+                    if (cb_date.isChecked) {
+                        timeText = "${cb_date.text} ${cb_time.text}"
+                        KMPDFStampTextView.TimeType.TIME_AND_DATE
+                    } else {
+                        timeText = "${cb_time.text}"
+                        KMPDFStampTextView.TimeType.TIME
+                    }
+                } else {
+                    if (cb_date.isChecked) {
+                        timeText = "${cb_date.text}"
+                        KMPDFStampTextView.TimeType.DATE
+                    } else {
+                        timeText = ""
+                        KMPDFStampTextView.TimeType.NULL
+                    }
+                }
+                StampAttribute(rect = rect, config = TextStampConfig(stampType.lineColor, stampType.backgroundColor, stampType.textColor, et_input.text.toString(), timeText, stampType.shape, timeType))
+            }
+            false -> {
+                StampAttribute(STANDARD_STAMP_RES.values()[selectedStandardStampIndex])
+            }
+        }
+    }
+
+    private inner class StampViewPagerAdapter(private val tabs: ArrayList<String>): PagerAdapter() {
+        private val layoutInflater = LayoutInflater.from(context)
+
+        override fun instantiateItem(container: ViewGroup, position: Int): Any {
+            val view = when (position) {
+                0 -> {
+                    val customStampView = layoutInflater.inflate(R.layout.view_viewer_edit_item_stamp_custom, container, false)
+
+                    val date = Date()
+                    customStampView.cb_date.text = SimpleDateFormat("yyyy/MM/dd").format(date)
+                    customStampView.cb_time.text = SimpleDateFormat("hh:mm:ss a").format(date).toLowerCase(Locale.US)
+
+                    val recyclerView = customStampView.recyclerView_shapeCustom
+                    recyclerView.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
+                    val adapter = CustomStampAdapter(context, CustomStampType.values())
+                    adapter.setOnStampClickListener(object : OnStampClickListener {
+                        override fun onStampClick(index: Int) {
+                            selectedCustomStampIndex = index
+                        }
+                    })
+                    recyclerView.adapter = adapter
+                    recyclerView.setHasFixedSize(true)
+                    customStampView
+                }
+                else -> {
+                    val standardStampView = layoutInflater.inflate(R.layout.view_viewer_edit_item_stamp_standard, container, false)
+                    val recyclerView = standardStampView.recyclerView_shapeStandard
+                    recyclerView.layoutManager = GridLayoutManager(context, 3)
+                    val adapter = StandardStampAdapter(context, STANDARD_STAMP_RES.values())
+                    adapter.setOnStampClickListener(object : OnStampClickListener {
+                        override fun onStampClick(index: Int) {
+                            selectedStandardStampIndex = index
+                        }
+                    })
+                    recyclerView.adapter = adapter
+                    recyclerView.setHasFixedSize(true)
+                    standardStampView
+                }
+            }
+            container.addView(view)
+            return view
+        }
+
+        override fun destroyItem(container: ViewGroup, position: Int, obj: Any) {
+            container.removeView(obj as View)
+        }
+
+        override fun getCount(): Int {
+            return tabs.size
+        }
+
+        override fun isViewFromObject(view: View, obj: Any): Boolean {
+            return view == obj
+        }
+
+        override fun getPageTitle(position: Int): CharSequence? {
+            return tabs[position]
+        }
+    }
+}

+ 46 - 0
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/StandardStampAdapter.kt

@@ -0,0 +1,46 @@
+package com.kdanmobile.reader.screen.view.edit
+
+import android.content.Context
+import android.support.v7.widget.RecyclerView
+import android.view.ViewGroup
+import android.widget.ImageView
+import com.kdanmobile.kmpdfkit.annotation.stamp.StampConfig
+
+class StandardStampAdapter(private val context: Context, private val resourceIds: Array<StampConfig.STANDARD_STAMP_RES>): RecyclerView.Adapter<StandardStampViewHolder>() {
+
+    private var selectedIndex = 0
+    private var onStampClickListener: OnStampClickListener? = null
+
+    override fun onBindViewHolder(holder: StandardStampViewHolder, position: Int) {
+
+        holder.imageView.setOnClickListener {
+            if (position != selectedIndex) {
+                val oldSelectedIndex = selectedIndex
+                selectedIndex = position
+                notifyItemChanged(selectedIndex)
+                notifyItemChanged(oldSelectedIndex)
+                onStampClickListener?.onStampClick(selectedIndex)
+            }
+        }
+        holder.imageView.setImageResource(resourceIds[position].resId)
+        holder.imageView.setBackgroundColor(when (position == selectedIndex) {
+            true -> 0xff80ffff.toInt()
+            false -> 0xffffffff.toInt()
+        })
+    }
+
+    override fun getItemCount(): Int {
+        return resourceIds.size
+    }
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StandardStampViewHolder {
+        val view = ImageView(context)
+        return StandardStampViewHolder(view)
+    }
+
+    fun setOnStampClickListener(onStampClickListener: OnStampClickListener?) {
+        this.onStampClickListener = onStampClickListener
+    }
+}
+
+class StandardStampViewHolder(val imageView: ImageView): RecyclerView.ViewHolder(imageView)

+ 45 - 8
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/TextBoxTabView.kt

@@ -1,15 +1,24 @@
 package com.kdanmobile.reader.screen.view.edit
 
 import android.content.Context
+import android.graphics.Typeface
 import android.support.constraint.ConstraintLayout
 import android.util.AttributeSet
 import android.view.LayoutInflater
 import android.widget.SeekBar
 import com.kdanmobile.reader.R
+import com.kdanmobile.reader.screen.data.TextBoxAttribute
+import com.kdanmobile.reader.screen.view.ColorSelectView
 import kotlinx.android.synthetic.main.view_viewer_edit_tab_text_box.view.*
 
 class TextBoxTabView : ConstraintLayout {
 
+    var onTitleButtonClickListener: TitleButton.OnTitleButtonClickListener? = null
+        set(value) {
+            field = value
+            titleButton_textBox?.onTitleButtonClickListener = field
+        }
+
     constructor(context: Context) : super(context) {
         initView()
     }
@@ -25,34 +34,62 @@ class TextBoxTabView : ConstraintLayout {
     private fun initView() {
         LayoutInflater.from(context).inflate(R.layout.view_viewer_edit_tab_text_box, this)
 
-        titleButton_textBox.onButtonClickListener = OnClickListener { _ ->
-            println("onButtonClick")
-        }
-
         textAttrView_textBox.onTextAttrClickListener = object : TextAttrView.OnTextAttrClickListener {
+
+            private fun getTypeface(): Int {
+                return when (textAttrView_textBox.isTextBold) {
+                    true -> when (textAttrView_textBox.isTextItalic) {
+                        true -> Typeface.BOLD_ITALIC
+                        false -> Typeface.BOLD
+                    }
+                    false -> when (textAttrView_textBox.isTextItalic) {
+                        true -> Typeface.ITALIC
+                        false -> Typeface.NORMAL
+                    }
+                }
+            }
+
             override fun onBoldClick(isSelected: Boolean) {
-                println("onBoldClick $isSelected")
+                tv_preview.setTypeface(null, getTypeface())
             }
 
             override fun onItalicClick(isSelected: Boolean) {
-                println("onItalicClick $isSelected")
+                tv_preview.setTypeface(null, getTypeface())
             }
 
             override fun onAlignmentClick(gravity: Int) {
-                println("onAlignmentClick $gravity")
+                tv_preview.gravity = gravity
+            }
+        }
+
+        colorChooser_textBox.onColorSelectedListener = object : ColorSelectView.OnColorSelectedListener {
+            override fun onColorSelected(color: Int) {
+                tv_preview.setTextColor(color)
             }
         }
 
         seekBar_textBox_textSize.onSeekBarChangeListener = object : SeekBar.OnSeekBarChangeListener {
             override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
-                println("onProgressChanged $progress")
+//                tv_preview.textSize = seekBar_textBox_textSize.currentValue.toFloat()
             }
 
             override fun onStartTrackingTouch(seekBar: SeekBar) {
             }
 
             override fun onStopTrackingTouch(seekBar: SeekBar) {
+//                tv_preview.textSize = seekBar_textBox_textSize.currentValue.toFloat()
             }
         }
+
+        tv_preview.setTextColor(colorChooser_textBox.getSelectedColor())
+    }
+
+    fun getTextBoxAttribute(): TextBoxAttribute {
+        val isBold = textAttrView_textBox.isTextBold
+        val isItalic = textAttrView_textBox.isTextItalic
+        val textAlignment = textAttrView_textBox.alignment
+        val textColor = tv_preview.currentTextColor
+        val textSize = seekBar_textBox_textSize.currentValue
+        return TextBoxAttribute(isBold, isItalic, textAlignment, textColor, textSize)
     }
 }

+ 17 - 1
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/TextSeekBar.kt

@@ -16,10 +16,26 @@ class TextSeekBar : ConstraintLayout {
     private var minValue = 0
     private var initValue = 0
 
+    private val defaultFormatter = object : SeekBarFormatter {
+        override fun format(value: Int): String {
+            return value.toString()
+        }
+    }
+
+    var formatter = defaultFormatter
+        set(value) {
+            field = value
+            tv_seekBarValue?.text = "${formatter.format(currentValue)}$unit"
+        }
+
+    interface SeekBarFormatter {
+        fun format(value: Int): String
+    }
+
     var currentValue = 0
         set(value) {
             field = Math.max(Math.min(value, maxValue), minValue)
-            tv_seekBarValue?.text = "$field$unit"
+            tv_seekBarValue?.text = "${formatter.format(currentValue)}$unit"
             seekBar?.progress = field - minValue
         }
     var onSeekBarChangeListener: SeekBar.OnSeekBarChangeListener? = null

+ 6 - 2
reader/src/main/java/com/kdanmobile/reader/screen/view/edit/TitleButton.kt

@@ -12,10 +12,10 @@ class TitleButton : ConstraintLayout {
     private var title = ""
     private var buttonText = ""
 
-    var onButtonClickListener: OnClickListener? = null
+    var onTitleButtonClickListener: OnTitleButtonClickListener? = null
         set(value) {
             field = value
-            btn_add?.setOnClickListener(value)
+            btn_add?.setOnClickListener{field?.onTitleButtonClick()}
         }
 
     constructor(context: Context) : super(context) {
@@ -44,4 +44,8 @@ class TitleButton : ConstraintLayout {
         tv_title.text = title
         btn_add.text = buttonText
     }
+
+    interface OnTitleButtonClickListener {
+        fun onTitleButtonClick()
+    }
 }

+ 280 - 0
reader/src/main/java/com/kdanmobile/reader/view/AnnotationAttributeView.kt

@@ -0,0 +1,280 @@
+package com.kdanmobile.reader.view
+
+import android.content.Context
+import android.support.v4.graphics.ColorUtils
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.PopupWindow
+import android.widget.SeekBar
+import com.kdanmobile.reader.R
+import com.kdanmobile.reader.Utils
+import com.kdanmobile.reader.annotationattribute.AnnotationAttribute
+import com.kdanmobile.reader.annotationattribute.AnnotationColor
+import com.kdanmobile.reader.annotationattribute.Brush
+import com.kdanmobile.reader.annotationattribute.InkAttribute
+import kotlinx.android.synthetic.main.view_reader_annotation_property.view.*
+
+class AnnotationAttributeView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : LinearLayout(context, attrs, defStyleAttr) {
+
+    companion object {
+        const val DEFAULT_OPACITY = 50
+        const val DEFAULT_THICKNESS = 5
+    }
+
+
+    init {
+        inflate(context, R.layout.view_reader_annotation_property, this)
+    }
+    var inkAttribute: InkAttribute
+        get() {
+            return InkAttribute(color, alpha, thickness.toFloat(), brush)
+        }
+        set(value) {
+            color = value.color
+            alpha = value.alpha
+            thickness = value.width.toInt()
+            brush = value.brush
+        }
+    var annotationAttribute: AnnotationAttribute
+        get() {
+            return AnnotationAttribute(color, alpha)
+        }
+        set(value) {
+            color = value.color
+            alpha = value.alpha
+        }
+    var isSimpleMode = true
+        set(value) {
+            field = value
+            update()
+        }
+    var onChangeListener: OnChangeListener? = null
+    private val colorBtnMap = HashMap<AnnotationColor, ImageView>().apply {
+        put(AnnotationColor.Color1, iv_readerAnnotationProperty_color1)
+        put(AnnotationColor.Color2, iv_readerAnnotationProperty_color2)
+        put(AnnotationColor.Color3, iv_readerAnnotationProperty_color3)
+        put(AnnotationColor.Color4, iv_readerAnnotationProperty_color4)
+        put(AnnotationColor.Color5, iv_readerAnnotationProperty_color5)
+        put(AnnotationColor.Color6, iv_readerAnnotationProperty_color6)
+        put(AnnotationColor.Color7, iv_readerAnnotationProperty_color7)
+        put(AnnotationColor.Color8, iv_readerAnnotationProperty_color8)
+        put(AnnotationColor.Color9, iv_readerAnnotationProperty_color9)
+        put(AnnotationColor.Color10, iv_readerAnnotationProperty_color10)
+    }
+    private var thickness: Int = DEFAULT_THICKNESS
+        set(value) {
+            field = value
+            updateThickness()
+        }
+    /**
+     * 1~255
+     */
+    private var alpha: Int
+        get() {
+            return Math.round(opacity.toFloat() / 100 * 255)
+        }
+        set(value) {
+            val progress = Math.round(value.toFloat() / 255 * 100)
+            seekBar_readerAnnotationProperty_opacity.progress = progress
+        }
+    /**
+     * 1~100
+     */
+    private var opacity: Int = DEFAULT_OPACITY
+    private var color: Int
+        get() {
+            return context.resources.getColor(colorEnum.colorResId)
+        }
+        set(value) {
+            AnnotationColor.values().forEach {
+                val c = context.resources.getColor(it.colorResId)
+                if (c == value) {
+                    colorEnum = it
+                    return
+                }
+            }
+        }
+    private var colorEnum: AnnotationColor = AnnotationColor.Color1
+        set(value) {
+            field = value
+            updateColor()
+            updateColorButtons()
+            onChangeListener?.onChange(this)
+        }
+    private val colorWithAlpha: Int
+        get() {
+            return  ColorUtils.setAlphaComponent(color, alpha)
+        }
+    private var brush = Brush.Fountain
+        set(value) {
+            if (field == value) return
+            field = value
+            update()
+            when (value) {
+                Brush.Fountain -> if (thickness > brush.maxSize) thickness = brush.maxSize
+                Brush.Marker -> thickness = brush.maxSize
+            }
+            onChangeListener?.onChange(this)
+        }
+    init {
+        setupOpacitySeekBar()
+        setupThicknessSeekBar()
+        setupColorButtons()
+        setupBrushes()
+        update()
+    }
+
+    private fun setupBrushes() {
+        iv_readerAnnotationProperty_brush1.setOnClickListener {
+            brush = Brush.Fountain
+        }
+        iv_readerAnnotationProperty_brush2.setOnClickListener {
+            brush = Brush.Marker
+        }
+    }
+
+    private fun setupThicknessSeekBar() {
+        seekBar_readerAnnotationProperty_thickness.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
+            override fun onProgressChanged(p0: SeekBar?, progress: Int, p2: Boolean) {
+                thickness = progress
+                updateThickness()
+            }
+
+            override fun onStartTrackingTouch(p0: SeekBar?) {
+            }
+
+            override fun onStopTrackingTouch(p0: SeekBar?) {
+                onChangeListener?.onChange(this@AnnotationAttributeView)
+            }
+        })
+    }
+
+    private fun setupColorButtons() {
+        colorBtnMap.values.forEach {
+            it.setOnClickListener(this::onClickColorBtn)
+        }
+    }
+
+    private fun setupOpacitySeekBar() {
+        seekBar_readerAnnotationProperty_opacity.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
+            val colorDisplayerView = ColorOpacityDisplayerView(context)
+            val windowW = ViewGroup.LayoutParams.WRAP_CONTENT
+            val windowH = ViewGroup.LayoutParams.WRAP_CONTENT
+            val window = PopupWindow(colorDisplayerView, windowW, windowH).apply {
+                animationStyle = android.R.style.Animation
+            }
+            val contentView = window.contentView.apply {
+                val w = Utils.makeDropDownMeasureSpec(window.width)
+                val h = Utils.makeDropDownMeasureSpec(window.height)
+                measure(w, h)
+            }
+
+            override fun onProgressChanged(seekBar: SeekBar, progress: Int, p2: Boolean) {
+                opacity = progress
+                val x = calculateOffsetX(seekBar)
+                val y = calculateOffsetY(seekBar)
+                window.update(seekBar, x, y, window.width, window.height)
+                updateOpacity()
+                colorDisplayerView.color = colorWithAlpha
+            }
+
+            override fun onStartTrackingTouch(seekBar: SeekBar) {
+                val x = calculateOffsetX(seekBar)
+                val y = calculateOffsetY(seekBar)
+                colorDisplayerView.color = colorWithAlpha
+                window.showAsDropDown(seekBar, x, y)
+            }
+
+            override fun onStopTrackingTouch(p0: SeekBar?) {
+                window.dismiss()
+                onChangeListener?.onChange(this@AnnotationAttributeView)
+            }
+
+            private fun calculateOffsetX(seekBar: SeekBar): Int {
+                val seekBarLineWidth = seekBar.width - seekBar.paddingLeft - seekBar.paddingRight
+                val progress = seekBar.progress.toFloat() / seekBar.max.toFloat()
+                return (seekBar.paddingLeft + seekBarLineWidth * progress - contentView.measuredWidth / 2).toInt()
+            }
+
+            private fun calculateOffsetY(seekBar: SeekBar): Int {
+                return -seekBar.height - contentView.measuredHeight
+            }
+        })
+    }
+
+    private fun onClickColorBtn(view: View) {
+        colorBtnMap.entries.forEach {
+            if (it.value == view) {
+                colorEnum = it.key
+                return
+            }
+        }
+    }
+
+    private fun update() {
+        updateOpacity()
+        updateThickness()
+        updateColor()
+        updateColorButtons()
+        group_readerAnnotationProperty_brushMode.visibility = if (isSimpleMode) View.GONE else View.VISIBLE
+        group_readerAnnotationProperty_brushMode.post {
+            when (brush) {
+                Brush.Fountain -> {
+                    iv_readerAnnotationProperty_triangle1.visibility = View.VISIBLE
+                    iv_readerAnnotationProperty_triangle2.visibility = View.INVISIBLE
+                }
+                Brush.Marker -> {
+                    iv_readerAnnotationProperty_triangle1.visibility = View.INVISIBLE
+                    iv_readerAnnotationProperty_triangle2.visibility = View.VISIBLE
+                }
+            }
+        }
+    }
+
+    private fun updateOpacity() {
+        seekBar_readerAnnotationProperty_opacity.progress = opacity
+        tv_readerAnnotationProperty_opacityValue.text = opacity.toString()
+        waveLineView_readerAnnotationProperty.setColor(colorWithAlpha)
+    }
+
+    private fun updateThickness() {
+        when (brush) {
+            Brush.Fountain -> {
+                seekBar_readerAnnotationProperty_thickness.max = brush.maxSize
+                seekBar_readerAnnotationProperty_thickness.isEnabled = true
+            }
+            Brush.Marker -> {
+                seekBar_readerAnnotationProperty_thickness.max = brush.maxSize
+                seekBar_readerAnnotationProperty_thickness.isEnabled = false
+            }
+        }
+        seekBar_readerAnnotationProperty_thickness.progress = thickness
+        tv_readerAnnotationProperty_thicknessValue.text = thickness.toString()
+        waveLineView_readerAnnotationProperty.setThickness(thickness.toFloat())
+    }
+
+    private fun updateColor() {
+        waveLineView_readerAnnotationProperty.setColor(colorWithAlpha)
+    }
+
+    private fun updateColorButtons() {
+        colorBtnMap.entries.forEach {
+            if (it.key == colorEnum) {
+                it.value.setImageResource(R.drawable.reader_annotation_property_color_border_select)
+            } else {
+                it.value.setImageResource(R.drawable.reader_annotation_property_color_border)
+            }
+        }
+    }
+
+    interface OnChangeListener {
+        fun onChange(annotationAttributeView: AnnotationAttributeView)
+    }
+}

+ 22 - 0
reader/src/main/java/com/kdanmobile/reader/view/AnnotationAttributeWindow.kt

@@ -0,0 +1,22 @@
+package com.kdanmobile.reader.view
+
+import android.content.Context
+import android.os.Build
+import android.widget.PopupWindow
+import com.kdanmobile.reader.R
+
+class AnnotationAttributeWindow(context: Context, isSimpleMode: Boolean) : PopupWindow(context) {
+    val annotationAttributeView: AnnotationAttributeView
+
+    init {
+        isOutsideTouchable = true
+        isFocusable = true
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
+            elevation = context.resources.getDimension(R.dimen.reader_annotation_property_setting_window_elevation)
+        }
+        setBackgroundDrawable(context.resources.getDrawable(R.drawable.bg_annotation_property_setting_window))
+        annotationAttributeView = AnnotationAttributeView(context)
+        annotationAttributeView.isSimpleMode = isSimpleMode
+        contentView = annotationAttributeView
+    }
+}

+ 30 - 0
reader/src/main/java/com/kdanmobile/reader/view/ColorOpacityDisplayerView.kt

@@ -0,0 +1,30 @@
+package com.kdanmobile.reader.view
+
+import android.content.Context
+import android.graphics.Color
+import android.support.constraint.ConstraintLayout
+import android.support.v4.graphics.ColorUtils
+import android.util.AttributeSet
+import android.view.View
+import com.kdanmobile.reader.R
+import kotlinx.android.synthetic.main.view_color_opacity_displayer.view.*
+
+class ColorOpacityDisplayerView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0
+) : ConstraintLayout(context, attrs, defStyleAttr) {
+    init {
+        View.inflate(context, R.layout.view_color_opacity_displayer, this)
+    }
+
+    var color = Color.BLACK
+        set(value) {
+            field = value
+            update()
+        }
+
+    private fun update() {
+        view_colorOpacityDisplayer_color.setBackgroundColor(color)
+    }
+}

+ 1 - 1
reader/src/main/java/com/kdanmobile/reader/HorizontalView.java

@@ -1,4 +1,4 @@
-package com.kdanmobile.reader;
+package com.kdanmobile.reader.view;
 
 import android.app.Activity;
 import android.content.Context;

+ 82 - 0
reader/src/main/java/com/kdanmobile/reader/view/WaveLineView.java

@@ -0,0 +1,82 @@
+package com.kdanmobile.reader.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Cap;
+import android.graphics.Paint.Style;
+import android.graphics.Path;
+import android.support.annotation.NonNull;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * @类名:WaveLineView
+ * @类描述:自定义控件实现波浪线
+ * @作者:zhouguifang
+ * @创建时间:2015-7-7-上午11:35:01
+ * @修改人:
+ * @修改时间:
+ * @修改备注:
+ * @版本:
+ * @Copyright:(c)-2015kdan mobile
+ */
+public class WaveLineView extends View {
+	private Paint paint;
+	private Path path;
+	private int color = 0xffff0000;
+	private float thickness = 20;
+	private float bili = 1.f / 6;
+
+	public WaveLineView(Context context, AttributeSet attrs) {
+		super(context, attrs);
+		paint = new Paint();
+		paint.setColor(color);
+		paint.setAntiAlias(true);
+		paint.setStrokeCap(Cap.ROUND);
+		paint.setStyle(Style.STROKE);
+		paint.setStrokeWidth(20f);
+	}
+
+	public void setThickness(float thickness) {
+		this.thickness = thickness;
+		invalidate();
+	}
+
+	public void setColor(int color) {
+		this.color = color;
+		invalidate();
+	}
+
+	@Override
+	public void draw(@NonNull Canvas canvas) {
+		super.draw(canvas);
+		if (getWidth() > 0) {
+			if (path == null) {
+				int w = getWidth();
+				int h = getHeight();
+				path = new Path();
+				path.moveTo(0, h / 2f);
+				path.lineTo((w * 30) / 360, (h / 2) - (h * bili * 0.5f));
+				path.lineTo((w * 45) / 360, (h / 2) - (h * bili * 0.707f));
+				path.lineTo((w * 60) / 360, (h / 2) - (h * bili * 0.866f));
+				path.lineTo((w * 90) / 360, (h / 2) - (h * bili * 1.0f));
+				path.lineTo((w * 120) / 360, (h / 2) - (h * bili * 0.866f));
+				path.lineTo((w * 135) / 360, (h / 2) - (h * bili * 0.707f));
+				path.lineTo((w * 150) / 360, (h / 2) - (h * bili * 0.5f));
+				path.lineTo((w * 180) / 360, (h / 2) - (h * bili * 0.0f));
+				path.lineTo((w * 210) / 360, (h / 2) + (h * bili * 0.5f));
+				path.lineTo((w * 225) / 360, (h / 2) + (h * bili * 0.707f));
+				path.lineTo((w * 240) / 360, (h / 2) + (h * bili * 0.866f));
+				path.lineTo((w * 270) / 360, (h / 2) + (h * bili * 1.0f));
+				path.lineTo((w * 300) / 360, (h / 2) + (h * bili * 0.866f));
+				path.lineTo((w * 315) / 360, (h / 2) + (h * bili * 0.707f));
+				path.lineTo((w * 330) / 360, (h / 2) + (h * bili * 0.5f));
+				path.lineTo((w * 360) / 360, (h / 2) + (h * bili * 0.0f));
+			}
+			paint.setColor(color);
+			paint.setStrokeWidth(thickness);
+			canvas.drawPath(path, paint);
+		}
+	}
+}

BIN
reader/src/main/res/drawable-hdpi/ic_chevron_left.png


BIN
reader/src/main/res/drawable-hdpi/ic_chevron_right.png


BIN
reader/src/main/res/drawable-hdpi/ic_freehand_triangle.png


BIN
reader/src/main/res/drawable-hdpi/ic_kdan_cloud.png


BIN
reader/src/main/res/drawable-hdpi/ic_view_all.png


BIN
reader/src/main/res/drawable-hdpi/panel_property_color_preview.png


BIN
reader/src/main/res/drawable-hdpi/rectangle_pic_arrow.webp


BIN
reader/src/main/res/drawable-hdpi/rectangle_pic_circular.webp


BIN
reader/src/main/res/drawable-hdpi/rectangle_pic_line.webp


BIN
reader/src/main/res/drawable-hdpi/rectangle_pic_rectangle.webp


BIN
reader/src/main/res/drawable-hdpi/signature_ic_add.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_blue_left_arrow.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_blue_rect.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_blue_right_arrow.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_green_left_arrow.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_green_rect.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_green_right_arrow.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_red_left_arrow.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_red_rect.webp


BIN
reader/src/main/res/drawable-hdpi/stamp_red_right_arrow.webp


BIN
reader/src/main/res/drawable-mdpi/rectangle_pic_arrow.webp


BIN
reader/src/main/res/drawable-mdpi/rectangle_pic_circular.webp


BIN
reader/src/main/res/drawable-mdpi/rectangle_pic_line.webp


BIN
reader/src/main/res/drawable-mdpi/rectangle_pic_rectangle.webp


BIN
reader/src/main/res/drawable-mdpi/signature_ic_add.webp


BIN
reader/src/main/res/drawable-xhdpi/ic_chevron_left.png


BIN
reader/src/main/res/drawable-xhdpi/ic_chevron_right.png


BIN
reader/src/main/res/drawable-xhdpi/ic_kdan_cloud.png


BIN
reader/src/main/res/drawable-xhdpi/ic_view_all.png


BIN
reader/src/main/res/drawable-xhdpi/panel_property_color_preview.png


BIN
reader/src/main/res/drawable-xhdpi/rectangle_pic_arrow.webp


BIN
reader/src/main/res/drawable-xhdpi/rectangle_pic_circular.webp


BIN
reader/src/main/res/drawable-xhdpi/rectangle_pic_line.webp


BIN
reader/src/main/res/drawable-xhdpi/rectangle_pic_rectangle.webp


BIN
reader/src/main/res/drawable-xhdpi/signature_ic_add.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_blue_left_arrow.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_blue_rect.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_blue_right_arrow.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_green_left_arrow.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_green_rect.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_green_right_arrow.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_red_left_arrow.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_red_rect.webp


BIN
reader/src/main/res/drawable-xhdpi/stamp_red_right_arrow.webp


+ 5 - 0
reader/src/main/res/drawable-xxhdpi/bg_annotation_property_setting_window.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#f5f5f5"/>
+    <corners android:radius="4dp"/>
+</shape>

BIN
reader/src/main/res/drawable-xxhdpi/ic_chevron_left.png


BIN
reader/src/main/res/drawable-xxhdpi/ic_chevron_right.png


BIN
reader/src/main/res/drawable-xxhdpi/ic_freehand_triangle.png


BIN
reader/src/main/res/drawable-xxhdpi/ic_kdan_cloud.png


BIN
reader/src/main/res/drawable-xxhdpi/ic_view_all.png


BIN
reader/src/main/res/drawable-xxhdpi/rectangle_pic_arrow.webp


BIN
reader/src/main/res/drawable-xxhdpi/rectangle_pic_circular.webp


BIN
reader/src/main/res/drawable-xxhdpi/rectangle_pic_line.webp


BIN
reader/src/main/res/drawable-xxhdpi/rectangle_pic_rectangle.webp


+ 0 - 0
reader/src/main/res/drawable-xxhdpi/seekbar_back.png


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.