Jelajahi Sumber

Merge branch 'whiteNativeContextMenu' into 'master'

白色context menu

See merge request kdanandroid/pdf/pdfreaderreadermodule!21
Wayne Huang 5 tahun lalu
induk
melakukan
b03fc48a3f

+ 27 - 0
src/main/java/android/support/v7/widget/MyRoundRectDrawableWithShadow.java

@@ -0,0 +1,27 @@
+package android.support.v7.widget;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+
+public class MyRoundRectDrawableWithShadow extends RoundRectDrawableWithShadow {
+    public MyRoundRectDrawableWithShadow(Resources resources, int backgroundColor, float radius, float shadowSize, float maxShadowSize) {
+        super(resources, getColor(backgroundColor), radius, shadowSize, maxShadowSize);
+        initRoundRectHelper();
+    }
+
+    private static ColorStateList getColor(int color) {
+        return new ColorStateList(new int[][]{new int[]{android.R.attr.state_enabled}, new int[]{-android.R.attr.state_enabled}, new int[]{-android.R.attr.state_checked}, new int[]{android.R.attr.state_pressed}}, new int[]{color, color, color, color});
+    }
+
+    private void initRoundRectHelper() {
+        RoundRectDrawableWithShadow.sRoundRectHelper = new RoundRectDrawableWithShadow.RoundRectHelper() {
+            @Override
+            public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, Paint paint) {
+                canvas.drawRoundRect(bounds, cornerRadius, cornerRadius, paint);
+            }
+        };
+    }
+}

+ 14 - 103
src/main/java/com/kdanmobile/reader/ReaderViewModel.kt

@@ -9,8 +9,6 @@ import android.net.Uri
 import android.support.v4.content.ContextCompat
 import com.kdanmobile.kmpdfkit.annotation.Annotation
 import com.kdanmobile.kmpdfkit.annotation.bean.*
-import com.kdanmobile.kmpdfkit.contextmenu.KMPDFMenuItem
-import com.kdanmobile.kmpdfkit.contextmenu.MenuItemCallback
 import com.kdanmobile.kmpdfkit.globaldata.AnnotConfig
 import com.kdanmobile.kmpdfkit.globaldata.Config
 import com.kdanmobile.kmpdfkit.globaldata.KMPDFAnnotEditMode
@@ -22,6 +20,7 @@ import com.kdanmobile.kmpdfkit.pdfcommon.*
 import com.kdanmobile.reader.annotationattribute.AnnotationAttribute
 import com.kdanmobile.reader.annotationattribute.Brush
 import com.kdanmobile.reader.annotationattribute.InkAttribute
+import com.kdanmobile.reader.screen.contextmenu.MyKMPDFContextMenuCallback
 import com.kdanmobile.reader.screen.contextmenu.TextBoxContextMenuActionListener
 import com.kdanmobile.reader.screen.data.ShapeAttribute
 import com.kdanmobile.reader.screen.data.SignatureAttribute
@@ -173,6 +172,14 @@ class ReaderViewModel(private val readerModelManager: ReaderModelManager, val ur
         SUCCESS, FAILED, PASSWORD_PROTECTED
     }
 
+    private val contextMenuCallback by lazy {
+        object : MyKMPDFContextMenuCallback(kmpdfFactory) {
+            override fun onLongPress() {
+                annotationEitModeLiveData.postValue(ReaderViewModel.AnnotationEitMode.FREETEXT_MODIFY)
+            }
+        }
+    }
+
     @JvmOverloads
     fun openPdfFile(context: Context, password: String, onRequestPassword: Runnable, type: String? = null): OpenFileResult {
         if (!isVerified) {
@@ -432,116 +439,20 @@ class ReaderViewModel(private val readerModelManager: ReaderModelManager, val ur
             annotationEitModeLiveData.postValue(AnnotationEitMode.getMode(it))
         }
 
-        setLongPressContextMenuActions()
-        setSignatureContextMenuActions()
-        setStampContextMenuActions()
-        setShapeContextMenuActions()
-        setInkMenuActions()
-        setMarkerUpMenuActions()
+        setupContextMenu()
 
         if (!isFirst) {
             restoreStateBeforeDestroy()
         }
     }
 
-    private fun setLongPressContextMenuActions() {
-        val item = KMPDFMenuItem()
-        item.annotType = KMPDFMenuItem.AnnotType.LONG_PRESS
-        item.menu_resId = R.menu.context_menu_longpress
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            (kmpdfFactory?.getController(KMPDFFactory.ControllerType.LONGCLICK) as KMPDFLongPressCreateAnnotController).longPress_Paste()
-            annotationEitModeLiveData.postValue(AnnotationEitMode.FREETEXT_MODIFY)
-            return@MenuItemCallback true
-        })
-        kmpdfFactory?.setAnnotationContextMenu(item)
-    }
-
-    private fun setSignatureContextMenuActions() {
-        val item = KMPDFMenuItem()
-        item.annotType = KMPDFMenuItem.AnnotType.SIGNATURE
-        item.menu_resId = R.menu.context_menu_signature
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            (kmpdfFactory?.getController(KMPDFFactory.ControllerType.SIGNATURE) as KMPDFSignatureController).deleteSignatureAnnotView()
-            return@MenuItemCallback true
-        })
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            (kmpdfFactory?.getController(KMPDFFactory.ControllerType.SIGNATURE) as KMPDFSignatureController).saveSignatureAnnot()
-            return@MenuItemCallback true
-        })
-        kmpdfFactory?.setAnnotationContextMenu(item)
-    }
-
-    private fun setStampContextMenuActions() {
-        val item = KMPDFMenuItem()
-        item.annotType = KMPDFMenuItem.AnnotType.STAMP
-        item.menu_resId = R.menu.context_menu_normal
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            (kmpdfFactory?.getController(KMPDFFactory.ControllerType.STAMP) as KMPDFStampController).deleteStampAnnotView()
-            return@MenuItemCallback true
-        })
-        kmpdfFactory?.setAnnotationContextMenu(item)
-    }
-
-    private fun setShapeContextMenuActions() {
-        val item = KMPDFMenuItem()
-        item.annotType = KMPDFMenuItem.AnnotType.LINE
-        item.menu_resId = R.menu.context_menu_normal
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            (kmpdfFactory?.getController(KMPDFFactory.ControllerType.SHAPE) as KMPDFShapeAnnotController).deleteShapeAnnotView()
-            return@MenuItemCallback true
-        })
-        kmpdfFactory?.setAnnotationContextMenu(item)
-    }
-
-    private fun setInkMenuActions() {
-        val item = KMPDFMenuItem()
-        item.annotType = KMPDFMenuItem.AnnotType.INK
-        item.menu_resId = R.menu.context_menu_normal
-        item.menuCallbacks.add(MenuItemCallback { _, _ ->
-            kmpdfFactory?.let {
-                val controller = it.getController(KMPDFFactory.ControllerType.INK) as KMPDFInkController
-                controller.deleteInkAnnotView()
-                return@MenuItemCallback true
-            }
-            return@MenuItemCallback false
-        })
-        kmpdfFactory?.setAnnotationContextMenu(item)
-    }
-
-    private fun setMarkerUpMenuActions() {
-        val item = KMPDFMenuItem()
-        /** Just set HIGH_LIGHT then highlight, strike, underline will be triggered. **/
-        /** WTF !!!!!!!!!!! **/
-        item.annotType = KMPDFMenuItem.AnnotType.HIGH_LIGHT
-        item.menu_resId = R.menu.context_menu_normal
-        item.menuCallbacks.add(MenuItemCallback { _, _ ->
-            kmpdfFactory?.let {
-                val controller = it.getController(KMPDFFactory.ControllerType.MARKER_UP) as KMPDFMarkupController
-                controller.deleteMarkupAnnotView()
-                return@MenuItemCallback true
-            }
-            return@MenuItemCallback false
-        })
-        kmpdfFactory?.setAnnotationContextMenu(item)
+    private fun setupContextMenu() {
+        val controller = kmpdfFactory?.getController(KMPDFFactory.ControllerType.CONTEXT_MENU) as KMPDFContextMenuController?
+        controller?.contextMenuCallback = contextMenuCallback
     }
 
     fun setTextBoxContextMenuActions(listener: TextBoxContextMenuActionListener) {
-        val item = KMPDFMenuItem()
-        item.annotType = KMPDFMenuItem.AnnotType.FREETEXT
-        item.menu_resId = R.menu.context_menu_textbox
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            return@MenuItemCallback listener.onDelete()
-        })
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            return@MenuItemCallback listener.onEditStyle()
-        })
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            return@MenuItemCallback listener.onEditText()
-        })
-        item.menuCallbacks.add(MenuItemCallback { view, annotType ->
-            return@MenuItemCallback listener.onCopy()
-        })
-        kmpdfFactory?.setAnnotationContextMenu(item)
+        contextMenuCallback.setTextBoxContextMenuActions(listener)
     }
 
     private fun restoreStateBeforeDestroy() {

+ 314 - 0
src/main/java/com/kdanmobile/reader/screen/contextmenu/MyKMPDFContextMenuCallback.kt

@@ -0,0 +1,314 @@
+package com.kdanmobile.reader.screen.contextmenu
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Point
+import android.graphics.PointF
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.StateListDrawable
+import android.os.Build
+import android.support.v4.content.ContextCompat
+import android.support.v7.widget.MyRoundRectDrawableWithShadow
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.LinearLayout
+import android.widget.PopupWindow
+import android.widget.TextView
+import com.kdanmobile.kmpdfkit.annotation.freeText.view.KMPDFFreeTextEditView
+import com.kdanmobile.kmpdfkit.manager.KMPDFFactory
+import com.kdanmobile.kmpdfkit.manager.controller.*
+import com.kdanmobile.kmpdfkit.manager.listener.KMPDFContextMenuCallback
+import com.kdanmobile.reader.R
+import com.kdanmobile.reader.utils.DensityUtil
+
+open class MyKMPDFContextMenuCallback(private val kmpdfFactory: KMPDFFactory?) : KMPDFContextMenuCallback {
+
+    private var popupWindow: PopupWindow? = null
+
+    private var textBoxContextMenuActionListener: TextBoxContextMenuActionListener? = null
+
+    open fun onLongPress() {}
+
+    override fun onDismissContextMenu(type: KMPDFContextMenuCallback.ContextMenuType?, view: View?) {
+        dismissPopupWindow()
+    }
+
+    override fun onShowContextMenu(type: KMPDFContextMenuCallback.ContextMenuType?, view: View?) {
+        if (null != view && null == popupWindow) {
+
+            //  create context menu depended on ContextMenuType
+            val context = view.context!!
+            val contentView = when (type) {
+                KMPDFContextMenuCallback.ContextMenuType.Markup -> createMarkupContextMenuView(context)
+                KMPDFContextMenuCallback.ContextMenuType.INK -> createInkContextMenuView(context)
+                KMPDFContextMenuCallback.ContextMenuType.SelectText -> createSelectTextContextMenuView(context)
+                KMPDFContextMenuCallback.ContextMenuType.Shape -> createShapeContextMenuView(context)
+                KMPDFContextMenuCallback.ContextMenuType.Freetext -> createTextBoxContextMenuView(context)
+                KMPDFContextMenuCallback.ContextMenuType.Signature -> createSignatureContextMenuView(context)
+                KMPDFContextMenuCallback.ContextMenuType.Stamp -> createStampContextMenuView(context)
+                KMPDFContextMenuCallback.ContextMenuType.LongPress -> createLongPressContextMenuView(context)
+                else -> LinearLayout(context)
+            }
+
+            //  LongPress on TextBox
+            if (type == KMPDFContextMenuCallback.ContextMenuType.Freetext) {
+                if (view is KMPDFFreeTextEditView) {
+                    view.setOnLoogPressListener {
+                        dismissPopupWindow()
+                    }
+                }
+            }
+
+            val position = computeDisplayPosition(context, type, view, contentView)
+
+            popupWindow = PopupWindow(contentView, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true)
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+                popupWindow?.elevation = context.resources.getDimension(R.dimen.reader_context_menu_window_elevation)
+            } else {
+                setElevationBelowAndroidKitKat(context, contentView)
+            }
+            popupWindow?.isOutsideTouchable = false
+            popupWindow?.isFocusable = false
+            popupWindow?.showAtLocation(view, Gravity.LEFT or Gravity.TOP, position.x, position.y)
+        }
+    }
+
+    private fun computeDisplayPosition(context: Context, type: KMPDFContextMenuCallback.ContextMenuType?, view: View, contentView: View): Point {
+        //  get the size of context menu
+        contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+        val windowWidth = contentView.measuredWidth
+        val windowHeight = contentView.measuredHeight
+
+        //  get position y offset and margin of context menu
+        val margin = context.resources.getDimensionPixelSize(R.dimen.reader_context_menu_window_position_margin)
+        val offset = context.resources.getDimensionPixelSize(when (type) {
+            KMPDFContextMenuCallback.ContextMenuType.Markup,
+            KMPDFContextMenuCallback.ContextMenuType.INK -> R.dimen.reader_context_menu_window_position_offset_markup
+            KMPDFContextMenuCallback.ContextMenuType.SelectText -> R.dimen.reader_context_menu_window_position_offset_select_text
+            KMPDFContextMenuCallback.ContextMenuType.Shape,
+            KMPDFContextMenuCallback.ContextMenuType.Freetext,
+            KMPDFContextMenuCallback.ContextMenuType.Signature,
+            KMPDFContextMenuCallback.ContextMenuType.Stamp -> R.dimen.reader_context_menu_window_position_offset_annotation
+            else -> R.dimen.reader_context_menu_window_position_offset
+        })
+
+        //  get location of view
+        val anchorLoc = arrayOf(0, 0).toIntArray()
+        view.getLocationOnScreen(anchorLoc)
+
+        //  compute the position of context menu
+        val screenHeight = DensityUtil.getScreenHeightPx(context)
+        val screenWidth = DensityUtil.getScreenWidthPx(context)
+        val topPositionY = anchorLoc[1] - windowHeight - offset
+        val bottomPositionY = anchorLoc[1] + view.height + offset
+        val leftPositionX = anchorLoc[0] - windowWidth - offset
+        val rightPositionX = anchorLoc[0] + view.width + offset
+
+        //  determine the final position of context menu
+        val positionY = when {
+            topPositionY - margin > 0 -> topPositionY                                                               //  top
+            screenHeight - windowHeight - bottomPositionY - margin > 0 -> bottomPositionY                           //  bottom
+            else -> (topPositionY + bottomPositionY) / 2                                                            //  center
+        }
+        val positionX = when {
+            positionY == topPositionY || positionY == bottomPositionY -> (leftPositionX + rightPositionX) / 2       //  center
+            leftPositionX - margin > 0 -> leftPositionX                                                             //  left
+            screenWidth - windowWidth - rightPositionX - margin > 0 -> rightPositionX                               //  right
+            else -> (leftPositionX + rightPositionX) / 2                                                            //  center
+        }
+
+        return Point(positionX, positionY)
+    }
+
+    private fun dismissPopupWindow() {
+        popupWindow?.dismiss()
+        popupWindow = null
+    }
+
+    private fun setElevationBelowAndroidKitKat(context: Context, view: View) {
+        val elevation = context.resources.getDimension(R.dimen.reader_context_menu_window_elevation)
+        val drawable = MyRoundRectDrawableWithShadow(context.resources, Color.TRANSPARENT, 0f, elevation, elevation)
+        setViewBackground(view, drawable)
+    }
+
+    private fun setViewBackground(view: View, drawable: Drawable) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+            view.background = drawable
+        } else {
+            view.setBackgroundDrawable(drawable)
+        }
+    }
+
+    private fun createTextBoxSelector(context: Context, topLeft: Boolean, topRight: Boolean, bottomRight: Boolean, bottomLeft: Boolean): Drawable {
+        val radius = context.resources.getDimension(R.dimen.reader_context_menu_text_box_radius)
+        val topLeftRadius = if (topLeft) PointF(radius, radius) else PointF()
+        val topRightRadius = if (topRight) PointF(radius, radius) else PointF()
+        val bottomRightRadius = if (bottomRight) PointF(radius, radius) else PointF()
+        val bottomLeftRadius = if (bottomLeft) PointF(radius, radius) else PointF()
+        val cornerRadii = floatArrayOf(topLeftRadius.x, topLeftRadius.y, topRightRadius.x, topRightRadius.y, bottomRightRadius.x, bottomRightRadius.y, bottomLeftRadius.x, bottomLeftRadius.y)
+
+        val normalDrawable = GradientDrawable()
+        normalDrawable.cornerRadii = cornerRadii
+        normalDrawable.setColor(ContextCompat.getColor(context, R.color.reader_contextMenu_bgNormal))
+        val pressedDrawable = GradientDrawable()
+        pressedDrawable.cornerRadii = cornerRadii
+        pressedDrawable.setColor(ContextCompat.getColor(context, R.color.reader_contextMenu_bgPressed))
+        val drawable = StateListDrawable()
+        drawable.addState(intArrayOf(android.R.attr.state_pressed), pressedDrawable)
+        drawable.addState(intArrayOf(android.R.attr.state_enabled), normalDrawable)
+        return drawable
+    }
+
+    private fun createMarkupContextMenuView(context: Context): View {
+        /** Just set HIGH_LIGHT then highlight, strike, underline will be triggered. **/
+        /** WTF !!!!!!!!!!! **/
+        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        val contentView = layoutInflater.inflate(R.layout.view_context_menu_normal, null)
+        contentView.findViewById<TextView>(R.id.btnDelete_contextMenu).setOnClickListener {
+            val kmpdfMarkupController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.MARKER_UP) as KMPDFMarkupController?
+            kmpdfMarkupController?.deleteMarkupAnnotView()
+            dismissPopupWindow()
+        }
+        return contentView
+    }
+
+    private fun createInkContextMenuView(context: Context): View {
+        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        val contentView = layoutInflater.inflate(R.layout.view_context_menu_normal, null)
+        contentView.findViewById<TextView>(R.id.btnDelete_contextMenu).setOnClickListener {
+            val kmpdfInkController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.INK) as KMPDFInkController?
+            kmpdfInkController?.deleteInkAnnotView()
+            dismissPopupWindow()
+        }
+        return contentView
+    }
+
+    private fun createSelectTextContextMenuView(context: Context): View {
+        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        val contentView = layoutInflater.inflate(R.layout.view_context_menu_select_text, null)
+
+        val btnCopy = contentView.findViewById<TextView>(R.id.btnCopy_contextMenu)
+        val btnHighlight = contentView.findViewById<TextView>(R.id.btnHighlight_contextMenu)
+        val btnUnderline = contentView.findViewById<TextView>(R.id.btnUnderline_contextMenu)
+        val btnStrikeout = contentView.findViewById<TextView>(R.id.btnStrikeout_contextMenu)
+
+        setViewBackground(btnCopy, createTextBoxSelector(context, true, false, false, true))
+        setViewBackground(btnHighlight, createTextBoxSelector(context, false, false, false, false))
+        setViewBackground(btnUnderline, createTextBoxSelector(context, false, false, false, false))
+        setViewBackground(btnStrikeout, createTextBoxSelector(context, false, true, true, false))
+
+        val kmpdfSelectTextController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.SELECT_TEXT) as KMPDFSelectTextController?
+        btnCopy.setOnClickListener {
+            kmpdfSelectTextController?.copySelectedText()
+            dismissPopupWindow()
+        }
+        btnHighlight.setOnClickListener {
+            kmpdfSelectTextController?.highlightSelectedText()
+            dismissPopupWindow()
+        }
+        btnUnderline.setOnClickListener {
+            kmpdfSelectTextController?.underlineSelectedText()
+            dismissPopupWindow()
+        }
+        btnStrikeout.setOnClickListener {
+            kmpdfSelectTextController?.strikeoutSelectedText()
+            dismissPopupWindow()
+        }
+        return contentView
+    }
+
+    private fun createShapeContextMenuView(context: Context): View {
+        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        val contentView = layoutInflater.inflate(R.layout.view_context_menu_normal, null)
+        contentView.findViewById<TextView>(R.id.btnDelete_contextMenu).setOnClickListener {
+            val kmpdfShapeAnnotController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.SHAPE) as KMPDFShapeAnnotController?
+            kmpdfShapeAnnotController?.deleteShapeAnnotView()
+            dismissPopupWindow()
+        }
+        return contentView
+    }
+
+    private fun createTextBoxContextMenuView(context: Context): View {
+        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        val contentView = layoutInflater.inflate(R.layout.view_context_menu_textbox, null)
+
+        val btnDelete = contentView.findViewById<TextView>(R.id.btnDelete_contextMenu)
+        val btnStyle = contentView.findViewById<TextView>(R.id.btnStyle_contextMenu)
+        val btnEdit = contentView.findViewById<TextView>(R.id.btnEdit_contextMenu)
+        val btnCopy = contentView.findViewById<TextView>(R.id.btnCopy_contextMenu)
+
+        setViewBackground(btnDelete, createTextBoxSelector(context, true, false, false, true))
+        setViewBackground(btnStyle, createTextBoxSelector(context, false, false, false, false))
+        setViewBackground(btnEdit, createTextBoxSelector(context, false, false, false, false))
+        setViewBackground(btnCopy, createTextBoxSelector(context, false, true, true, false))
+
+        btnDelete.setOnClickListener {
+            textBoxContextMenuActionListener?.onDelete()
+            dismissPopupWindow()
+        }
+        btnStyle.setOnClickListener {
+            textBoxContextMenuActionListener?.onEditStyle()
+            dismissPopupWindow()
+        }
+        btnEdit.setOnClickListener {
+            textBoxContextMenuActionListener?.onEditText()
+            dismissPopupWindow()
+        }
+        btnCopy.setOnClickListener {
+            textBoxContextMenuActionListener?.onCopy()
+            dismissPopupWindow()
+        }
+        return contentView
+    }
+
+    private fun createSignatureContextMenuView(context: Context): View {
+        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        val contentView = layoutInflater.inflate(R.layout.view_context_menu_signature, null)
+
+        val btnDelete = contentView.findViewById<TextView>(R.id.btnDelete_contextMenu)
+        val btnAdd = contentView.findViewById<TextView>(R.id.btnAdd_contextMenu)
+
+        setViewBackground(btnDelete, createTextBoxSelector(context, true, false, false, true))
+        setViewBackground(btnAdd, createTextBoxSelector(context, false, true, true, false))
+
+        val kmpdfSignatureController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.SIGNATURE) as KMPDFSignatureController?
+        btnDelete.setOnClickListener {
+            kmpdfSignatureController?.deleteSignatureAnnotView()
+            dismissPopupWindow()
+        }
+        btnAdd.setOnClickListener {
+            kmpdfSignatureController?.saveSignatureAnnot()
+            dismissPopupWindow()
+        }
+        return contentView
+    }
+
+    private fun createStampContextMenuView(context: Context): View {
+        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        val contentView = layoutInflater.inflate(R.layout.view_context_menu_normal, null)
+        contentView.findViewById<TextView>(R.id.btnDelete_contextMenu).setOnClickListener {
+            val kmpdfStampController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.STAMP) as KMPDFStampController?
+            kmpdfStampController?.deleteStampAnnotView()
+            dismissPopupWindow()
+        }
+        return contentView
+    }
+
+    private fun createLongPressContextMenuView(context: Context): View {
+        val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
+        val contentView = layoutInflater.inflate(R.layout.view_context_menu_long_press, null)
+        contentView.findViewById<TextView>(R.id.btnPaste_contextMenu).setOnClickListener {
+            val kmpdfLongPressCreateAnnotController = kmpdfFactory?.getController(KMPDFFactory.ControllerType.LONGCLICK) as KMPDFLongPressCreateAnnotController?
+            kmpdfLongPressCreateAnnotController?.longPress_Paste()
+            onLongPress()
+            dismissPopupWindow()
+        }
+        return contentView
+    }
+
+    fun setTextBoxContextMenuActions(listener: TextBoxContextMenuActionListener) {
+        textBoxContextMenuActionListener = listener
+    }
+}

+ 5 - 0
src/main/res/drawable/bg_context_menu_item.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@drawable/bg_context_menu_item_pressed" android:state_pressed="true"/>
+    <item android:drawable="@drawable/bg_context_menu_item_normal"/>
+</selector>

+ 5 - 0
src/main/res/drawable/bg_context_menu_item_normal.xml

@@ -0,0 +1,5 @@
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+    <solid android:color="@color/reader_contextMenu_bgNormal" />
+    <corners android:radius="@dimen/reader_context_menu_text_box_radius"/>
+</shape>

+ 5 - 0
src/main/res/drawable/bg_context_menu_item_pressed.xml

@@ -0,0 +1,5 @@
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+    <solid android:color="@color/reader_contextMenu_bgPressed" />
+    <corners android:radius="@dimen/reader_context_menu_text_box_radius"/>
+</shape>

+ 5 - 0
src/main/res/drawable/bg_context_menu_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="@color/reader_contextMenu_bgNormal"/>
+    <corners android:radius="@dimen/reader_context_menu_text_box_radius"/>
+</shape>

+ 13 - 0
src/main/res/layout/view_context_menu_long_press.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="@drawable/bg_context_menu_window">
+
+    <TextView
+        android:id="@+id/btnPaste_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/contextMenu_longPress_paste"/>
+</LinearLayout>

+ 13 - 0
src/main/res/layout/view_context_menu_normal.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="@drawable/bg_context_menu_window">
+
+    <TextView
+        android:id="@+id/btnDelete_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/contextMenu_normal_delete"/>
+</LinearLayout>

+ 28 - 0
src/main/res/layout/view_context_menu_select_text.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="@drawable/bg_context_menu_window">
+
+    <TextView
+        android:id="@+id/btnCopy_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/context_menu_copy"/>
+
+    <TextView
+        android:id="@+id/btnHighlight_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/context_menu_highlight"/>
+
+    <TextView
+        android:id="@+id/btnUnderline_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/context_menu_underline"/>
+
+    <TextView
+        android:id="@+id/btnStrikeout_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/context_menu_strikeout"/>
+</LinearLayout>

+ 18 - 0
src/main/res/layout/view_context_menu_signature.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="@drawable/bg_context_menu_window">
+
+    <TextView
+        android:id="@+id/btnDelete_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/contextMenu_signature_delete"/>
+
+    <TextView
+        android:id="@+id/btnAdd_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/contextMenu_signature_add"/>
+</LinearLayout>

+ 28 - 0
src/main/res/layout/view_context_menu_textbox.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="@drawable/bg_context_menu_window">
+
+    <TextView
+        android:id="@+id/btnDelete_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/contextMenu_textbox_delete"/>
+
+    <TextView
+        android:id="@+id/btnStyle_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/contextMenu_textbox_style"/>
+
+    <TextView
+        android:id="@+id/btnEdit_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/contextMenu_textbox_edit"/>
+
+    <TextView
+        android:id="@+id/btnCopy_contextMenu"
+        style="@style/ContextMenuItemTheme"
+        android:text="@string/contextMenu_textbox_copy"/>
+</LinearLayout>

+ 0 - 7
src/main/res/menu/context_menu_longpress.xml

@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item
-        android:title="@string/contextMenu_longPress_paste" />
-
-</menu>

+ 0 - 7
src/main/res/menu/context_menu_normal.xml

@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item
-        android:title="@string/contextMenu_normal_delete" />
-
-</menu>

+ 0 - 10
src/main/res/menu/context_menu_signature.xml

@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item
-        android:title="@string/contextMenu_signature_delete" />
-
-    <item
-        android:title="@string/contextMenu_signature_add" />
-
-</menu>

+ 0 - 16
src/main/res/menu/context_menu_textbox.xml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item
-        android:title="@string/contextMenu_textbox_delete" />
-
-    <item
-        android:title="@string/contextMenu_textbox_style" />
-
-    <item
-        android:title="@string/contextMenu_textbox_edit" />
-
-    <item
-        android:title="@string/contextMenu_textbox_copy" />
-
-</menu>

+ 4 - 0
src/main/res/values/colors.xml

@@ -75,4 +75,8 @@
     <color name="reader_textbox_sample_text_border">#cccccc</color>
 
     <color name="ink_menu_background">#FFFFFF</color>
+
+    <color name="reader_contextMenu_text">#DE000000</color>
+    <color name="reader_contextMenu_bgNormal">#FFFFFF</color>
+    <color name="reader_contextMenu_bgPressed">#E0E0E0</color>
 </resources>

+ 8 - 0
src/main/res/values/dimens.xml

@@ -23,4 +23,12 @@
     <dimen name="material_dialog_title_body">20dp</dimen>
 
     <bool name="isReaderSettingFullScreen">true</bool>
+
+    <dimen name="reader_context_menu_window_position_offset_annotation">10dp</dimen>
+    <dimen name="reader_context_menu_window_position_offset_markup">20dp</dimen>
+    <dimen name="reader_context_menu_window_position_offset_select_text">-20dp</dimen>
+    <dimen name="reader_context_menu_window_position_offset">10dp</dimen>
+    <dimen name="reader_context_menu_window_position_margin">40dp</dimen>
+    <dimen name="reader_context_menu_window_elevation">4dp</dimen>
+    <dimen name="reader_context_menu_text_box_radius">4dp</dimen>
 </resources>

+ 11 - 1
src/main/res/values/styles.xml

@@ -51,7 +51,6 @@
         <item name="android:background">#1e000000</item>
     </style>
 
-
     <style name="MyCustomToolBarStyle" parent="@style/ThemeOverlay.AppCompat.Light">
         <item name="android:textColorPrimary">@color/normal_text</item>
         <item name="android:textColor">@color/normal_text</item>
@@ -88,4 +87,15 @@
     <style name="ForegroundTouchEffectBorderLess">
         <item name="android:foreground">?android:attr/selectableItemBackground</item>
     </style>
+
+    <style name="ContextMenuItemTheme">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_weight">1</item>
+        <item name="android:padding">10dp</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:singleLine">true</item>
+        <item name="android:textColor">@color/reader_contextMenu_text</item>
+        <item name="android:background">@drawable/bg_context_menu_item</item>
+    </style>
 </resources>