瀏覽代碼

[feat]键盘插件,实现确认弹窗UI

hezihao 8 月之前
父節點
當前提交
f307487537
共有 25 個文件被更改,包括 746 次插入19 次删除
  1. 1 1
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/component/QuickSwitchComponent.kt
  2. 8 0
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/component/ToolBarComponent.kt
  3. 13 0
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ext/DialogExt.kt
  4. 4 2
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/keyboard/CustomKeyboardService.kt
  5. 33 0
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ui/core/LayoutCallback.java
  6. 136 0
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ui/dialog/ConfirmDialog.kt
  7. 123 0
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ui/dialog/base/BaseDialog.java
  8. 192 0
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ui/dialog/base/bottomsheet/BaseBottomSheetDialog.java
  9. 2 2
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/popup/ModeSwitchPopupWindow.kt
  10. 9 3
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/popup/item/ModeListItemViewBinder.kt
  11. 11 11
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/KeyboardHolder.kt
  12. 39 0
      plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/PermissionDialogUtil.kt
  13. 5 0
      plugins/keyboard_android/android/src/main/res/anim/base_pop_win_content_fade_in.xml
  14. 5 0
      plugins/keyboard_android/android/src/main/res/anim/base_pop_win_content_fade_out.xml
  15. 7 0
      plugins/keyboard_android/android/src/main/res/drawable/bg_confirm_dialog.xml
  16. 7 0
      plugins/keyboard_android/android/src/main/res/drawable/bg_dialog_action_btn1.xml
  17. 11 0
      plugins/keyboard_android/android/src/main/res/drawable/bg_dialog_action_btn2.xml
  18. 5 0
      plugins/keyboard_android/android/src/main/res/layout/base_bottom_sheet_dialog.xml
  19. 3 0
      plugins/keyboard_android/android/src/main/res/layout/component_tool_bar.xml
  20. 73 0
      plugins/keyboard_android/android/src/main/res/layout/dialog_confirm.xml
  21. 二進制
      plugins/keyboard_android/android/src/main/res/mipmap-xxxhdpi/ic_dialog_close.png
  22. 4 0
      plugins/keyboard_android/android/src/main/res/values/colors.xml
  23. 32 0
      plugins/keyboard_android/android/src/main/res/values/dialog_style.xml
  24. 4 0
      plugins/keyboard_android/android/src/main/res/values/string.xml
  25. 19 0
      plugins/keyboard_android/android/src/main/res/values/styles.xml

+ 1 - 1
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/component/QuickSwitchComponent.kt

@@ -5,7 +5,7 @@ import android.util.AttributeSet
 import android.view.View
 import com.atmob.keyboard_android.R
 import com.atmob.keyboard_android.component.base.BaseUIComponent
-import com.atmob.keyboard_android.popup.ModeSwitchPopupWindow
+import com.atmob.keyboard_android.ui.popup.ModeSwitchPopupWindow
 
 /**
  * 快速切换组件

+ 8 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/component/ToolBarComponent.kt

@@ -5,6 +5,7 @@ import android.util.AttributeSet
 import android.view.View
 import com.atmob.keyboard_android.R
 import com.atmob.keyboard_android.component.base.BaseUIComponent
+import com.atmob.keyboard_android.ext.click
 
 /**
  * 工具类组件
@@ -12,13 +13,20 @@ import com.atmob.keyboard_android.component.base.BaseUIComponent
 class ToolBarComponent @JvmOverloads constructor(
     context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
 ) : BaseUIComponent(context, attrs, defStyleAttr) {
+    private lateinit var vKeyboardSwitchBtn: View
+
     override fun onInflateViewId(): Int {
         return R.layout.component_tool_bar
     }
 
     override fun findView(view: View) {
+        vKeyboardSwitchBtn = view.findViewById(R.id.keyboard_switch_btn)
     }
 
     override fun bindView(view: View) {
+        vKeyboardSwitchBtn.click {
+            // TODO: hezihao,测试权限确认弹窗
+            // PermissionDialogUtil.showPermissionDialog(context)
+        }
     }
 }

+ 13 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ext/DialogExt.kt

@@ -0,0 +1,13 @@
+package com.atmob.keyboard_android.ext
+
+import android.app.Dialog
+import android.view.Window
+import android.view.WindowManager
+
+/**
+ * 设置Service的WindowToken给Dialog
+ */
+fun Dialog.setServiceWindowToken(hostWindow: Window) {
+    this.window?.setType(WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG)
+    this.window?.attributes?.token = hostWindow.attributes.token
+}

+ 4 - 2
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/keyboard/CustomKeyboardService.kt

@@ -69,7 +69,6 @@ class CustomKeyboardService : InputMethodService() {
         LogUtil.d("onCreateInputView!")
         val keyboardView = layoutInflater.inflate(R.layout.keyboard_layout, null)
         vKeyboardView = keyboardView
-        KeyboardHolder.attachKeyboardRootView(keyboardView)
 
         // 获取按键映射
         fetchKeyMappings()
@@ -79,12 +78,15 @@ class CustomKeyboardService : InputMethodService() {
 
     override fun onDestroy() {
         super.onDestroy()
-        KeyboardHolder.detachKeyboardRootView()
+        KeyboardHolder.detachKeyboardWindow()
     }
 
     override fun onStartInputView(info: EditorInfo?, restarting: Boolean) {
         super.onStartInputView(info, restarting)
         LogUtil.d("onStartInputView: 重新加载键盘数据")
+
+        KeyboardHolder.attachKeyboardWindow(window!!.window!!)
+
         // 重新获取按键映射
         fetchKeyMappings()
     }

+ 33 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ui/core/LayoutCallback.java

@@ -0,0 +1,33 @@
+package com.atmob.keyboard_android.ui.core;
+
+import android.view.View;
+
+/**
+ * UI界面加载以及事件
+ */
+public interface LayoutCallback {
+    /**
+     * 设置布局之前回调
+     */
+    void onLayoutBefore();
+
+    /**
+     * 获取布局LayoutId
+     */
+    int onInflaterViewId();
+
+    /**
+     * 填充完毕View后回调
+     */
+    void onInflaterViewAfter(View view);
+
+    /**
+     * 查找View和给View进行相关设置等
+     */
+    void onBindView(View view);
+
+    /**
+     * 设置数据
+     */
+    void setData();
+}

+ 136 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ui/dialog/ConfirmDialog.kt

@@ -0,0 +1,136 @@
+package com.atmob.keyboard_android.ui.dialog
+
+import android.content.Context
+import android.view.View
+import android.widget.TextView
+import com.atmob.keyboard_android.R
+import com.atmob.keyboard_android.ext.click
+import com.atmob.keyboard_android.ui.dialog.base.BaseDialog
+
+/**
+ * 确认弹窗
+ */
+class ConfirmDialog(context: Context) : BaseDialog(context) {
+    private lateinit var vCloseBtn: View
+    private lateinit var vTitle: TextView
+    private lateinit var vContent: TextView
+    private lateinit var vRejectBtn: TextView
+    private lateinit var vAllowBtn: TextView
+
+    /**
+     * 数据模型
+     */
+    private var mDataModel: DataModel? = null
+
+    /**
+     * 操作回调
+     */
+    private var mOnActionCallback: OnActionCallback? = null
+
+    override fun onInflaterViewId(): Int {
+        return R.layout.dialog_confirm
+    }
+
+    override fun onInflaterViewAfter(view: View) {
+        super.onInflaterViewAfter(view)
+        vCloseBtn = view.findViewById(R.id.close_btn)
+        vTitle = view.findViewById(R.id.title)
+        vContent = view.findViewById(R.id.content)
+        vRejectBtn = view.findViewById(R.id.reject_btn)
+        vAllowBtn = view.findViewById(R.id.allow_btn)
+    }
+
+    override fun onBindView(view: View) {
+        super.onBindView(view)
+        vCloseBtn.click {
+            // 关闭
+            dismissDialog()
+        }
+        vRejectBtn.click {
+            // 拒绝
+            mOnActionCallback?.onReject()
+            dismissDialog()
+        }
+        vAllowBtn.click {
+            // 允许
+            mOnActionCallback?.onAllow()
+            dismissDialog()
+        }
+    }
+
+    /**
+     * 渲染
+     */
+    private fun render() {
+        mDataModel?.run {
+            vTitle.text = title
+            vContent.apply {
+                text = content
+                visibility = if (content.isBlank()) {
+                    View.GONE
+                } else {
+                    View.VISIBLE
+                }
+            }
+            // 拒绝
+            vRejectBtn.text = if (rejectBtnText.isBlank()) {
+                context.resources.getString(R.string.reject)
+            } else {
+                rejectBtnText
+            }
+            // 同意
+            vAllowBtn.text = if (allowBtnText.isBlank()) {
+                context.resources.getString(R.string.allow)
+            } else {
+                allowBtnText
+            }
+        }
+    }
+
+    override fun onSetupDialogFrameSize(
+        screenWidth: Int,
+        screenHeight: Int
+    ): IntArray {
+        return intArrayOf(screenWidth, screenHeight)
+    }
+
+    class DataModel(
+        // 标题
+        val title: String,
+        // 内容
+        val content: String = "",
+        // 拒绝按钮的文字
+        val rejectBtnText: String = "",
+        // 同意按钮的文字
+        val allowBtnText: String = ""
+    )
+
+    interface OnActionCallback {
+        /**
+         * 拒绝
+         */
+        fun onReject()
+
+        /**
+         * 同意
+         */
+        fun onAllow()
+    }
+
+    /**
+     * 绑定数据
+     */
+    fun bindDataModel(dataModel: DataModel): ConfirmDialog {
+        this.mDataModel = dataModel
+        render()
+        return this
+    }
+
+    /**
+     * 绑定操作回调
+     */
+    fun bindOnActionCallback(callback: OnActionCallback): ConfirmDialog {
+        this.mOnActionCallback = callback
+        return this
+    }
+}

+ 123 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ui/dialog/base/BaseDialog.java

@@ -0,0 +1,123 @@
+package com.atmob.keyboard_android.ui.dialog.base;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
+
+import com.atmob.keyboard_android.R;
+import com.atmob.keyboard_android.ui.core.LayoutCallback;
+
+
+/**
+ * 弹窗基类
+ */
+public abstract class BaseDialog extends Dialog implements LayoutCallback {
+    /**
+     * 内容View
+     */
+    private View vContentView;
+
+    public BaseDialog(@NonNull Context context) {
+        super(context, R.style.base_dialog);
+        init();
+    }
+
+    public BaseDialog(@NonNull Context context, int themeResId) {
+        super(context, themeResId);
+        init();
+    }
+
+    private void init() {
+        onLayoutBefore();
+        normalize();
+        vContentView = getLayoutInflater().inflate(onInflaterViewId(), null);
+        setContentView(vContentView);
+        onInflaterViewAfter(vContentView);
+        onBindView(vContentView);
+        setData();
+        //默认点击黑色背景不能关闭
+        setCanceledOnTouchOutside(false);
+    }
+
+    /**
+     * 正常化Dialog
+     */
+    private void normalize() {
+        fixBackground();
+        fixSize();
+    }
+
+    /**
+     * 修复背景
+     */
+    private void fixBackground() {
+        Window window = getWindow();
+        if (window == null) {
+            return;
+        }
+        //去掉白色的背景
+        window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+    }
+
+    /**
+     * 修复大小
+     */
+    private void fixSize() {
+        Window window = getWindow();
+        if (window == null) {
+            return;
+        }
+        //获取需要显示的宽、高
+        DisplayMetrics metrics = new DisplayMetrics();
+        getWindow().getWindowManager().getDefaultDisplay().getMetrics(metrics);
+        int[] frameSize = onSetupDialogFrameSize(metrics.widthPixels, metrics.heightPixels);
+        //去掉默认间距
+        window.getDecorView().setPadding(0, 0, 0, 0);
+        WindowManager.LayoutParams params = window.getAttributes();
+        params.width = frameSize[0];
+        params.height = frameSize[1];
+        window.setAttributes(params);
+    }
+
+    protected abstract int[] onSetupDialogFrameSize(int screenWidth, int screenHeight);
+
+    @Override
+    public void onLayoutBefore() {
+    }
+
+    @Override
+    public void onInflaterViewAfter(View view) {
+    }
+
+    @Override
+    public void onBindView(View view) {
+    }
+
+    @Override
+    public void dismiss() {
+        super.dismiss();
+        onDismiss();
+    }
+
+    @Override
+    public void setData() {
+    }
+
+    protected void onDismiss() {
+    }
+
+    public View getContentView() {
+        return vContentView;
+    }
+
+    public void dismissDialog() {
+        dismiss();
+    }
+}

+ 192 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/ui/dialog/base/bottomsheet/BaseBottomSheetDialog.java

@@ -0,0 +1,192 @@
+package com.atmob.keyboard_android.ui.dialog.base.bottomsheet;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.animation.Animation;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.TranslateAnimation;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
+import androidx.coordinatorlayout.widget.CoordinatorLayout;
+
+import com.atmob.keyboard_android.R;
+import com.atmob.keyboard_android.ui.dialog.base.BaseDialog;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+
+
+/**
+ * 通用底部弹出Dialog,只定义动画和位置,内容子类去定义
+ */
+public abstract class BaseBottomSheetDialog extends BaseDialog {
+    private BottomSheetBehavior<View> mBottomSheetBehavior;
+
+    public BaseBottomSheetDialog(@NonNull Context context) {
+        super(context, R.style.base_bottom_sheet_dialog_style);
+        //设置外层阴影可点击消失
+        setCanceledOnTouchOutside(true);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        //设置弹窗在底部
+        Window window = getWindow();
+        if (window != null) {
+            WindowManager.LayoutParams params = window.getAttributes();
+            params.gravity = Gravity.BOTTOM;
+            window.setAttributes(params);
+        }
+    }
+
+    @Override
+    protected int[] onSetupDialogFrameSize(int screenWidth, int screenHeight) {
+        int[] size = new int[2];
+        size[0] = screenWidth;
+        size[1] = ViewGroup.LayoutParams.WRAP_CONTENT;
+        return size;
+    }
+
+    @Override
+    public int onInflaterViewId() {
+        return R.layout.base_bottom_sheet_dialog;
+    }
+
+    /**
+     * 通知创建内容View
+     *
+     * @param inflater 填充工厂
+     * @param parent   容器View
+     */
+    protected abstract View onCreateContentView(LayoutInflater inflater, ViewGroup parent);
+
+    @CallSuper
+    @Override
+    public void onBindView(View view) {
+        CoordinatorLayout container = view.findViewById(R.id.content_container);
+        View contentView = onCreateContentView(getLayoutInflater(), container);
+        //下拉关闭
+        mBottomSheetBehavior = BottomSheetBehavior.from(contentView);
+        mBottomSheetBehavior.setHideable(true);
+        //设置展开高度,设置为屏幕的四分之三,这样才不会分2段展开,而是一段
+        int screenHeight = getScreenHeight(view.getContext());
+        mBottomSheetBehavior.setPeekHeight((screenHeight * 3) / 4);
+        //设置状态回调
+        mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
+            @Override
+            public void onStateChanged(@NonNull View bottomSheet, int newState) {
+                if (newState == BottomSheetBehavior.STATE_HIDDEN) {
+                    cancel();
+                }
+            }
+
+            @Override
+            public void onSlide(@NonNull View bottomSheet, float slideOffset) {
+            }
+        });
+        //添加进布局
+        container.addView(contentView);
+    }
+
+    @Override
+    public void setCancelable(boolean cancelable) {
+        super.setCancelable(cancelable);
+        if (mBottomSheetBehavior != null) {
+            mBottomSheetBehavior.setHideable(cancelable);
+        }
+    }
+
+    @Override
+    public void show() {
+        super.show();
+        animationShow();
+    }
+
+    @Override
+    public void dismiss() {
+        //这里将dismiss覆盖掉,不调用super,让动画结束后再调用callDismiss去调用dismiss
+        animationHide();
+    }
+
+    /**
+     * 调用真正的dismiss
+     */
+    private void callDismiss() {
+        super.dismiss();
+    }
+
+    /**
+     * 从底部向上滑动
+     */
+    private void animationShow() {
+        if ((getContentView()) == null) {
+            return;
+        }
+        TranslateAnimation animation = new TranslateAnimation(1, 0.0F,
+                1, 0.0F, 1, 1.0F, 1, 0.0F);
+        animation.setInterpolator(new DecelerateInterpolator());
+        animation.setDuration(180L);
+        animation.setFillAfter(true);
+        animation.setAnimationListener(new AnimationAdapter() {
+            @Override
+            public void onAnimationEnd(Animation animation) {
+                super.onAnimationEnd(animation);
+                if (mBottomSheetBehavior != null && mBottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN) {
+                    mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
+                }
+            }
+        });
+        getContentView().startAnimation(animation);
+    }
+
+    /**
+     * 向底部滑动
+     */
+    private void animationHide() {
+        if (getContentView() == null) {
+            return;
+        }
+        TranslateAnimation animation = new TranslateAnimation(1, 0.0F,
+                1, 0.0F, 1, 0.0F, 1, 1.0F);
+        animation.setInterpolator(new DecelerateInterpolator());
+        animation.setDuration(180L);
+        animation.setFillAfter(true);
+        animation.setAnimationListener(new AnimationAdapter() {
+            @Override
+            public void onAnimationEnd(Animation animation) {
+                //动画结束,关闭弹窗
+                callDismiss();
+            }
+        });
+        getContentView().startAnimation(animation);
+    }
+
+    private static class AnimationAdapter implements Animation.AnimationListener {
+
+        @Override
+        public void onAnimationStart(Animation animation) {
+        }
+
+        @Override
+        public void onAnimationEnd(Animation animation) {
+        }
+
+        @Override
+        public void onAnimationRepeat(Animation animation) {
+        }
+    }
+
+    public static DisplayMetrics getDisplayMetrics(Context context) {
+        return context.getResources().getDisplayMetrics();
+    }
+
+    public static int getScreenHeight(Context context) {
+        return getDisplayMetrics(context).heightPixels;
+    }
+}

+ 2 - 2
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/popup/ModeSwitchPopupWindow.kt

@@ -1,4 +1,4 @@
-package com.atmob.keyboard_android.popup
+package com.atmob.keyboard_android.ui.popup
 
 import android.annotation.SuppressLint
 import android.content.Context
@@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView
 import com.atmob.keyboard_android.R
 import com.atmob.keyboard_android.enums.Mode
 import com.atmob.keyboard_android.model.ModeItem
-import com.atmob.keyboard_android.popup.item.ModeListItemViewBinder
+import com.atmob.keyboard_android.ui.popup.item.ModeListItemViewBinder
 import com.blankj.utilcode.util.ConvertUtils
 import me.drakeet.multitype.Items
 import me.drakeet.multitype.MultiTypeAdapter

+ 9 - 3
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/popup/item/ModeListItemViewBinder.kt

@@ -1,4 +1,4 @@
-package com.atmob.keyboard_android.popup.item
+package com.atmob.keyboard_android.ui.popup.item
 
 import android.graphics.Typeface
 import android.view.LayoutInflater
@@ -20,7 +20,13 @@ class ModeListItemViewBinder(
         inflater: LayoutInflater,
         parent: ViewGroup
     ): InnerViewHolder {
-        return InnerViewHolder(inflater.inflate(R.layout.item_mode, parent, false))
+        return InnerViewHolder(
+            inflater.inflate(
+                R.layout.item_mode,
+                parent,
+                false
+            )
+        )
     }
 
     override fun onBindViewHolder(
@@ -44,7 +50,7 @@ class ModeListItemViewBinder(
         }
     }
 
-    inner class InnerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+    class InnerViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
         val vMode: TextView = itemView.findViewById(R.id.mode)
     }
 }

+ 11 - 11
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/KeyboardHolder.kt

@@ -1,30 +1,30 @@
 package com.atmob.keyboard_android.util
 
 import android.annotation.SuppressLint
-import android.view.View
+import android.view.Window
 
 @SuppressLint("StaticFieldLeak")
 object KeyboardHolder {
-    private var keyboardRootView: View? = null
+    private var keyboardWindow: Window? = null
 
     /**
-     * 保存软键盘的根View
+     * 保存软键盘的Window
      */
-    fun attachKeyboardRootView(view: View) {
-        this.keyboardRootView = view
+    fun attachKeyboardWindow(window: Window) {
+        this.keyboardWindow = window
     }
 
     /**
-     * 获取软键盘的根View
+     * 获取软键盘的Window
      */
-    fun getKeyboardRootView(): View? {
-        return this.keyboardRootView
+    fun getKeyboardWindow(): Window? {
+        return this.keyboardWindow
     }
 
     /**
-     * 移除软键盘的根View
+     * 移除软键盘的Window
      */
-    fun detachKeyboardRootView() {
-        this.keyboardRootView = null
+    fun detachKeyboardWindow() {
+        this.keyboardWindow = null
     }
 }

+ 39 - 0
plugins/keyboard_android/android/src/main/kotlin/com/atmob/keyboard_android/util/PermissionDialogUtil.kt

@@ -0,0 +1,39 @@
+package com.atmob.keyboard_android.util
+
+import android.content.Context
+import com.atmob.keyboard_android.R
+import com.atmob.keyboard_android.ext.setServiceWindowToken
+import com.atmob.keyboard_android.ui.dialog.ConfirmDialog
+
+/**
+ * 权限弹窗工具类
+ */
+class PermissionDialogUtil {
+    companion object {
+        /**
+         * 显示权限弹窗
+         */
+        fun showPermissionDialog(context: Context) {
+            ConfirmDialog(context)
+                .apply {
+                    setServiceWindowToken(KeyboardHolder.getKeyboardWindow()!!)
+                }
+                .bindDataModel(
+                    ConfirmDialog.DataModel(
+                        title = context.resources.getString(R.string.dialog_permission_tip_title),
+                        content = context.resources.getString(R.string.dialog_permission_tip_content),
+                        rejectBtnText = context.resources.getString(R.string.reject),
+                        allowBtnText = context.resources.getString(R.string.allow)
+                    )
+                )
+                .bindOnActionCallback(object : ConfirmDialog.OnActionCallback {
+                    override fun onReject() {
+                    }
+
+                    override fun onAllow() {
+                    }
+                })
+                .show()
+        }
+    }
+}

+ 5 - 0
plugins/keyboard_android/android/src/main/res/anim/base_pop_win_content_fade_in.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="150"
+    android:fromAlpha="0"
+    android:toAlpha="1" />

+ 5 - 0
plugins/keyboard_android/android/src/main/res/anim/base_pop_win_content_fade_out.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="150"
+    android:fromAlpha="1"
+    android:toAlpha="0" />

+ 7 - 0
plugins/keyboard_android/android/src/main/res/drawable/bg_confirm_dialog.xml

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

+ 7 - 0
plugins/keyboard_android/android/src/main/res/drawable/bg_dialog_action_btn1.xml

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

+ 11 - 0
plugins/keyboard_android/android/src/main/res/drawable/bg_dialog_action_btn2.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <gradient
+        android:angle="180"
+        android:endColor="@color/text_color_brand"
+        android:startColor="@color/text_color_auxiliary1" />
+
+    <corners android:radius="50dp" />
+</shape>

+ 5 - 0
plugins/keyboard_android/android/src/main/res/layout/base_bottom_sheet_dialog.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/content_container"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent" />

+ 3 - 0
plugins/keyboard_android/android/src/main/res/layout/component_tool_bar.xml

@@ -43,11 +43,14 @@
         app:layout_constraintTop_toTopOf="parent">
 
         <ImageView
+            android:id="@+id/pinyin_switch_btn"
             android:layout_width="34dp"
             android:layout_height="34dp"
+            android:visibility="gone"
             android:src="@mipmap/ic_pinyin_btn" />
 
         <ImageView
+            android:id="@+id/keyboard_switch_btn"
             android:layout_width="34dp"
             android:layout_height="34dp"
             android:layout_marginStart="10dp"

+ 73 - 0
plugins/keyboard_android/android/src/main/res/layout/dialog_confirm.xml

@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:background="@color/bg_dialog_dim">
+
+    <FrameLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center">
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@drawable/bg_confirm_dialog"
+            android:orientation="vertical"
+            android:paddingStart="16dp"
+            android:paddingTop="24dp"
+            android:paddingEnd="16dp"
+            android:paddingBottom="24dp">
+
+            <TextView
+                android:id="@+id/title"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:gravity="center"
+                android:maxLines="1"
+                android:text="@string/dialog_permission_tip_title"
+                android:textColor="@color/text_color_primary"
+                android:textSize="16sp"
+                android:textStyle="bold" />
+
+            <TextView
+                android:id="@+id/content"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                android:gravity="center"
+                android:lineSpacingMultiplier="1.2"
+                android:text="@string/dialog_permission_tip_content"
+                android:textColor="@color/text_color_primary"
+                android:textSize="14sp"
+                android:textStyle="normal" />
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:id="@+id/reject_btn"
+                    style="@style/dialog_action_btn"
+                    android:layout_marginEnd="8dp"
+                    android:text="@string/reject" />
+
+                <TextView
+                    android:id="@+id/allow_btn"
+                    style="@style/dialog_action_btn2"
+                    android:text="@string/allow" />
+            </LinearLayout>
+        </LinearLayout>
+
+        <ImageView
+            android:id="@+id/close_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:padding="14dp"
+            android:src="@mipmap/ic_dialog_close" />
+    </FrameLayout>
+</FrameLayout>

二進制
plugins/keyboard_android/android/src/main/res/mipmap-xxxhdpi/ic_dialog_close.png


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

@@ -34,4 +34,8 @@
     <color name="bg_permission_tip">#FFF8EF</color>
     <!-- 权限提示的文字颜色 -->
     <color name="text_permission_tip">#FFA616</color>
+    <!-- 弹窗背景阴暗颜色 -->
+    <color name="bg_dialog_dim">#26000000</color>
+    <!-- 弹窗的操作按钮背景颜色 -->
+    <color name="bg_dialog_action_btn_1">#F6F5FA</color>
 </resources>

+ 32 - 0
plugins/keyboard_android/android/src/main/res/values/dialog_style.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!-- 弹窗基类 -->
+    <style name="base_dialog" parent="@android:style/Theme.Dialog">
+        <item name="android:windowFrame">@null</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowIsFloating">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+        <item name="android:background">#00000000</item>
+        <item name="android:backgroundDimEnabled">true</item>
+        <item name="android:backgroundDimAmount">0.6</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowAnimationStyle">@style/base_dialog_animation</item>
+    </style>
+
+    <!-- 弹窗动画 -->
+    <style name="base_dialog_animation" parent="android:Animation.Dialog">
+        <item name="android:windowEnterAnimation">@anim/base_pop_win_content_fade_in</item>
+        <item name="android:windowExitAnimation">@anim/base_pop_win_content_fade_out</item>
+    </style>
+
+    <!-- 底部弹窗 -->
+    <style name="base_bottom_sheet_dialog_style" parent="android:Theme.Dialog">
+        <item name="android:backgroundDimAmount">0.6</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <!-- 为了避免在有 NavigationBar 的手机上 Dialog 从 NavigationBar 底部上来,所以去掉 Dialog 的动画,使用 View 的动画-->
+        <item name="android:windowAnimationStyle">@null</item>
+        <item name="android:layout_width">match_parent</item>
+    </style>
+</resources>

+ 4 - 0
plugins/keyboard_android/android/src/main/res/values/string.xml

@@ -19,4 +19,8 @@
     <string name="paste_tip">粘贴你想说的话,润色升华</string>
     <string name="re_generate">重新生成</string>
     <string name="paste_permission_tip">“允许”【从其他app粘贴】,不再弹出询问弹窗></string>
+    <string name="dialog_permission_tip_title">温馨提示</string>
+    <string name="dialog_permission_tip_content">“追爱小键盘”想从“微信”粘贴\n你允许这样做吗?</string>
+    <string name="reject">不允许</string>
+    <string name="allow">允许</string>
 </resources>

+ 19 - 0
plugins/keyboard_android/android/src/main/res/values/styles.xml

@@ -63,4 +63,23 @@
         <item name="android:background">@mipmap/bg_action_btn_2</item>
         <item name="android:textColor">@color/text_color_white</item>
     </style>
+
+    <!-- 弹窗操作按钮,拒绝 -->
+    <style name="dialog_action_btn">
+        <item name="android:layout_width">128dp</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:background">@drawable/bg_dialog_action_btn1</item>
+        <item name="android:gravity">center</item>
+        <item name="android:paddingTop">8dp</item>
+        <item name="android:paddingBottom">8dp</item>
+        <item name="android:textColor">@color/text_color_secondary</item>
+        <item name="android:textSize">16sp</item>
+        <item name="android:textStyle">normal</item>
+    </style>
+
+    <!-- 弹窗操作按钮,允许 -->
+    <style name="dialog_action_btn2" parent="dialog_action_btn">
+        <item name="android:background">@drawable/bg_dialog_action_btn2</item>
+        <item name="android:textColor">@color/text_color_white</item>
+    </style>
 </resources>