From 9772300a2b46af5a421172438d6e31936bbb5ec9 Mon Sep 17 00:00:00 2001 From: tumuyan Date: Sat, 8 Jan 2022 19:11:13 +0800 Subject: [PATCH] feat: add draft manager --- .../main/assets/rime/tongwenfeng.trime.yaml | 43 +++-- app/src/main/assets/rime/trime.yaml | 14 +- .../trime/clipboard/ClipboardAdapter.java | 18 +- .../com/osfans/trime/draft/DraftAdapter.java | 168 ++++++++++++++++++ .../com/osfans/trime/draft/DraftBean.java | 61 +++++++ .../java/com/osfans/trime/draft/DraftDao.java | 92 ++++++++++ .../com/osfans/trime/draft/DraftlHelper.java | 34 ++++ .../osfans/trime/ime/core/EditorInstance.kt | 20 +++ .../com/osfans/trime/ime/core/Preferences.kt | 16 +- .../java/com/osfans/trime/ime/core/Trime.java | 30 +++- .../trime/ime/enums/SymbolKeyboardType.kt | 3 + .../ime/keyboard/InputFeedbackManager.kt | 2 +- .../trime/ime/symbol/LiquidKeyboard.java | 66 ++++++- .../trime/settings/fragments/OtherFragment.kt | 5 + .../java/com/osfans/trime/setup/Config.java | 26 ++- app/src/main/res/values-zh-rCN/strings.xml | 2 + app/src/main/res/values-zh-rTW/strings.xml | 2 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/other_preference.xml | 19 +- 19 files changed, 571 insertions(+), 52 deletions(-) create mode 100644 app/src/main/java/com/osfans/trime/draft/DraftAdapter.java create mode 100644 app/src/main/java/com/osfans/trime/draft/DraftBean.java create mode 100644 app/src/main/java/com/osfans/trime/draft/DraftDao.java create mode 100644 app/src/main/java/com/osfans/trime/draft/DraftlHelper.java diff --git a/app/src/main/assets/rime/tongwenfeng.trime.yaml b/app/src/main/assets/rime/tongwenfeng.trime.yaml index d3dac69026..ffbf57441b 100644 --- a/app/src/main/assets/rime/tongwenfeng.trime.yaml +++ b/app/src/main/assets/rime/tongwenfeng.trime.yaml @@ -858,20 +858,23 @@ liquid_keyboard: single_width: 60 #single类型的按键宽度 vertical_gap: 8 #纵向按键间隙 margin_x: 2 #左右按键间隙的1/2 - keyboards: [emoji, math, ascii, cn, clipboard, history, exit, list , table, symbol, ids , pinyin, jp, unit, exit, grease, rusa, korea, lation, yinbiao, exit] #tab列表 + keyboards: [emoji, math, ascii, cn, history, clipboard, draft, list, exit, ids , jp, table, symbol, pinyin, grease, rusa, korea, lation, yinbiao, unit, exit] #tab列表 exit: name: 返回 type: NO_KEY keys: EXIT + history: + name: 常用 + type: HISTORY emoji: type: SINGLE - keys: "🙂😂🤣😆🙃😅🙈🙉🙊☹😑😄🤐😨😱🌚🌝🤔❤💔♡🌹💣👌👍😣😥😮🙄😏😕😯😪😫😴😌🤑😉😋😎😍😘😚😛😜😝😒😓😔😲😷🤒😇🤓🤗🤕🙁😖😞😟😤😢😭😦😧😨😩😬😰😳😵😡😠☝✌🖕👎🙏🤘👏💪💋☘🍀🌸☕🍵🍺🍻🍦🍬🍚🍜🍲🍖🎂💤" + keys: "🙂😂🤣😆🙃😅🥺🙈🙉🙊☹😑😄🤐😨😱🌚🌝🤔❤💔🌹💣👌👍😣😥😮🙄😏😕😯😪😫😴😌🤑😉😋😎😍😘😚😛😜😝😒😓😔😲😷🤒😇🤓🤗🤕🙁😖😞😟😤😢😭😦😧😨😩😬😰😳😵😡😠☝✌🖕👎🙏🤘👏💪💋☘🍀🌸☕🍵🍺🍻🍦🍬🍚🍜🍲🍖🎂💤" clipboard: type: CLIPBOARD name: 剪贴 - history: - name: 最近 - type: HISTORY + draft: + type: DRAFT + name: 草稿 math: #tab名称 type: SINGLE name: 数学 @@ -910,6 +913,7 @@ liquid_keyboard: - ~ symbol: name: 特殊 + type: SINGLE keys: "△▽○◇□☆▲▼●◆■★▷◁▶◀♻♲†⚝✡⚹✦✸✹�×⌫☑☒✅❎✔✘✓✗☼☽♀☻◐㏂☀☾♂☹◑㏘☜☝☞☚☟☛▪•‥…∷※♩♪♫♬§°♭♯♮‖¶№◎¤۞℗®©卍卐℡™㏇Φ↖↑↗◤㊤◥←↔→㊧㊥㊨↙↓↘◣㊦◢⇄⇅⇆⇤↩⇥❏❐◲〼▢▣⇦⇧⇨⇩⇪↶▸◂▴▾✁↷✍⏍ϟ📝✎✆☱☰☴⚿⛮⚙☲☯☵⛶☩☐☳☷☶💬🗨⟲ღ✈☂🎤🌐🔍" unit: name: 单位 @@ -950,7 +954,7 @@ liquid_keyboard: ids: type: SINGLE name: IDS - keys: "⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻" + keys: "⿰⿱⿲⿳⿴⿵⿶⿷⿸⿹⿺⿻↷↔" jp: type: SINGLE name: 假名 @@ -3906,28 +3910,31 @@ preset_keys: CommitScriptText: {label: 编码, send: Shift+Return} CommitComment: {label: 编码, send: Control+Shift+Return} DeleteCandidate: {label: 删词, send: Control+Delete, functional: false} + #切换键盘 + Keyboard_letter: { label: 字母, functional: true, send: Eisu_toggle, select: default } + Keyboard_default: { label: 返回, functional: false, send: Eisu_toggle, select: .default } + Keyboard_switch: { label: 键盘, functional: true, send: Eisu_toggle, select: .next } + Keyboard_number: { label: 123, functional: false, send: Eisu_toggle, select: number } + Keyboard_numberb: { label: 123, functional: false, send: Eisu_toggle, select: numberb } + Keyboard_symbols: { label: 更多, functional: false, send: Eisu_toggle, select: symbols } + Keyboard_move: { label: ❖, functional: true, send: Eisu_toggle, select: move } + Keyboard_next: { label: 后退, functional: false, send: Eisu_toggle, select: .next } + Keyboard_last: { label: 后退, functional: false, send: Eisu_toggle, select: .last } + Keyboard_last_lock: { label: 返回, send: Eisu_toggle, select: .last_lock } #直接切換到下一键盘 + liquid_keyboard_switch: { label: 更多, send: function, command: liquid_keyboard, option: "特殊" } + liquid_keyboard_switch2: { toggle: _liquid_keyboard, send: Mode_switch, states: [ 更多, 更多 ] } + liquid_keyboard_emoji: { label: 🙂, send: function, command: liquid_keyboard, option: "emoji" } + liquid_keyboard_clipboard: { label: 剪贴, send: function, command: liquid_keyboard, option: "剪贴" } # rime状态 Mode_switch: {toggle: ascii_mode, functional: false, send: Mode_switch, states: [ 中文, 西文 ]} Zenkaku_Hankaku: {toggle: full_shape, send: Mode_switch, states: [ 半角, 全角 ]} Henkan: {toggle: simplification, send: Mode_switch, states: [ 汉字, 汉字 ]} Charset_switch: {toggle: extended_charset, send: Mode_switch, states: [ 常用, 增广 ]} Punct_switch: {toggle: ascii_punct, send: Mode_switch, states: [ 。,, ., ]} - liquid_keyboard_switch: { toggle: _liquid_keyboard, send: Mode_switch, states: [ 更多, 更多 ] } - liquid_keyboard_clipboard: {label: 剪贴板, send: function, command: liquid_keyboard, option: "剪贴"} # trime设置 IME_switch: {label: 🌐, send: LANGUAGE_SWITCH} #彈出對話框選擇輸入法 IME_last: {label: 上一输入法, send: LANGUAGE_SWITCH, select: .last} #直接切換到上一輸入法 IME_next: {label: 下一输入法, send: LANGUAGE_SWITCH, select: .next} #直接切換到下一輸入法 - Keyboard_letter: {label: 字母, functional: true, send: Eisu_toggle, select: default} - Keyboard_default: {label: 返回, functional: false, send: Eisu_toggle, select: .default} - Keyboard_switch: {label: 键盘, functional: true, send: Eisu_toggle, select: .next} - Keyboard_number: {label: 123, functional: false, send: Eisu_toggle, select: number} - Keyboard_numberb: {label: 123, functional: false, send: Eisu_toggle, select: numberb} - Keyboard_symbols: {label: 更多, functional: false, send: Eisu_toggle, select: symbols} - Keyboard_move: {label: ❖, functional: true, send: Eisu_toggle, select: move} - Keyboard_next: {label: 后退, functional: false, send: Eisu_toggle, select: .next} - Keyboard_last: {label: 后退, functional: false, send: Eisu_toggle, select: .last} - Keyboard_last_lock: {label: 返回, send: Eisu_toggle, select: .last_lock} #直接切換到下一键盘 Schema_switch: {label: 下一方案, functional: false, send: Control+Shift+1} Schema_Eng: {label: En, functional: true, send: Control+Shift+0} Theme_settings: {label: 主题, send: SETTINGS, option: "theme"} diff --git a/app/src/main/assets/rime/trime.yaml b/app/src/main/assets/rime/trime.yaml index d5a30bac2c..1a001dd018 100644 --- a/app/src/main/assets/rime/trime.yaml +++ b/app/src/main/assets/rime/trime.yaml @@ -921,17 +921,21 @@ preset_keys: Henkan: {toggle: simplification, send: Mode_switch, states: [ 漢字, 汉字 ]} Charset_switch: {toggle: extended_charset, send: Mode_switch, states: [ 常用, 增廣 ]} Punct_switch: {toggle: ascii_punct, send: Mode_switch, states: [ 。,, ., ]} - # trime設定 - IME_switch: {label: 🌐, send: LANGUAGE_SWITCH} #彈出對話框選擇輸入法 - IME_last: {label: 上一輸入法, send: LANGUAGE_SWITCH, select: .last} #直接切換到上一輸入法 - IME_next: {label: 下一輸入法, send: LANGUAGE_SWITCH, select: .next} #直接切換到下一輸入法 + #切换键盘 Keyboard_symbols: {label: 符號, send: Eisu_toggle, select: symbols} Keyboard_number: {label: 數字, send: Eisu_toggle, select: number} Keyboard_letter: {label: 字母, send: Eisu_toggle, select: default} Keyboard_default: {label: 返回, send: Eisu_toggle, select: .default} Keyboard_switch: {label: 鍵盤, send: Eisu_toggle, select: .next} + liquid_keyboard_switch: { label: 更多, send: function, command: liquid_keyboard, option: "特殊" } + liquid_keyboard_switch2: { toggle: _liquid_keyboard, send: Mode_switch, states: [ 更多, 更多 ] } + liquid_keyboard_emoji: { label: 🙂, send: function, command: liquid_keyboard, option: "emoji" } + liquid_keyboard_clipboard: { label: 剪贴, send: function, command: liquid_keyboard, option: "剪贴" } + # trime設定 + IME_switch: { label: 🌐, send: LANGUAGE_SWITCH } #彈出對話框選擇輸入法 + IME_last: { label: 上一輸入法, send: LANGUAGE_SWITCH, select: .last } #直接切換到上一輸入法 + IME_next: { label: 下一輸入法, send: LANGUAGE_SWITCH, select: .next } #直接切換到下一輸入法 Schema_switch: {label: 下一方案, send: Control+Shift+1} - liquid_keyboard_switch: { toggle: _liquid_keyboard, send: Mode_switch, states: [ 更多, 更多 ] } one_hand_switch_1: {toggle: _one_hand_mode_1, send: Mode_switch, states: [ 左手, 普通 ]} one_hand_switch_2: {toggle: _one_hand_mode_2, send: Mode_switch, states: [ 右手, 普通 ]} one_hand_switch_3: {toggle: _one_hand_mode_3, send: Mode_switch, states: [ 左手, 右手 ]} diff --git a/app/src/main/java/com/osfans/trime/clipboard/ClipboardAdapter.java b/app/src/main/java/com/osfans/trime/clipboard/ClipboardAdapter.java index c559282944..e827c1080d 100644 --- a/app/src/main/java/com/osfans/trime/clipboard/ClipboardAdapter.java +++ b/app/src/main/java/com/osfans/trime/clipboard/ClipboardAdapter.java @@ -26,7 +26,7 @@ public class ClipboardAdapter extends RecyclerView.Adapter itemlist) { myContext = context; @@ -43,17 +43,13 @@ public void configStyle(int keyMarginX, int keyMarginTop) { this.keyMarginTop = keyMarginTop; // 边框尺寸、圆角、字号直接读取主题通用参数。配色优先读取 liquidKeyboard 专用参数。 - Config config = Config.get(myContext); + config = Config.get(myContext); textColor = config.getLiquidColor("long_text_color"); if (textColor == null) textColor = config.getLiquidColor("key_text_color"); textSize = config.getFloat("key_long_text_size"); if (textSize <= 0) textSize = config.getFloat("label_text_size"); - background = - config.getDrawable( - "long_text_back_color", "key_border", "key_long_text_border", "round_corner", null); - textFont = config.getFont("long_text_font"); } @@ -86,7 +82,10 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int in ((ClipboardAdapter.ItemViewHolder) viewHolder); if (textFont != null) itemViewHold.mTitle.setTypeface(textFont); - itemViewHold.mTitle.setText(searchHistoryBean.getText()); + + String text = searchHistoryBean.getText(); + if (text.length() > 300) itemViewHold.mTitle.setText(text.substring(0, 300)); + else itemViewHold.mTitle.setText(text); if (textSize > 0) itemViewHold.mTitle.setTextSize(textSize); @@ -109,8 +108,11 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int in } - if (background != null) itemViewHold.listItemLayout.setBackground(background); + Drawable background = + config.getDrawable( + "long_text_back_color", "key_border", "key_long_text_border", "round_corner", null); + if (background != null) itemViewHold.listItemLayout.setBackground(background); // 如果设置了回调,则设置点击事件 if (mOnItemClickListener != null) { itemViewHold.listItemLayout.setOnClickListener( diff --git a/app/src/main/java/com/osfans/trime/draft/DraftAdapter.java b/app/src/main/java/com/osfans/trime/draft/DraftAdapter.java new file mode 100644 index 0000000000..9a8d1982ee --- /dev/null +++ b/app/src/main/java/com/osfans/trime/draft/DraftAdapter.java @@ -0,0 +1,168 @@ +package com.osfans.trime.draft; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Typeface; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import com.blankj.utilcode.util.ToastUtils; +import com.google.android.flexbox.FlexboxLayoutManager; +import com.osfans.trime.R; +import com.osfans.trime.ime.symbol.SimpleKeyBean; +import com.osfans.trime.setup.Config; +import java.util.List; + +public class DraftAdapter extends RecyclerView.Adapter { + private final Context myContext; + private final List list; + private int keyMarginX, keyMarginTop; + private Integer textColor; + private float textSize; + private Typeface textFont; + private Drawable background; + + public DraftAdapter(Context context, List itemlist) { + myContext = context; + list = itemlist; + } + + @Override + public int getItemCount() { + return list.size(); + } + + public void configStyle(int keyMarginX, int keyMarginTop) { + this.keyMarginX = keyMarginX; + this.keyMarginTop = keyMarginTop; + + // 边框尺寸、圆角、字号直接读取主题通用参数。配色优先读取 liquidKeyboard 专用参数。 + Config config = Config.get(myContext); + textColor = config.getLiquidColor("long_text_color"); + if (textColor == null) textColor = config.getLiquidColor("key_text_color"); + + textSize = config.getFloat("key_long_text_size"); + if (textSize <= 0) textSize = config.getFloat("label_text_size"); + + background = + config.getDrawable( + "long_text_back_color", "key_border", "key_long_text_border", "round_corner", null); + + textFont = config.getFont("long_text_font"); + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(myContext).inflate(R.layout.simple_key_item, parent, false); + return new ItemViewHolder(view); + } + + private static class ItemViewHolder extends RecyclerView.ViewHolder { + public ItemViewHolder(View view) { + super(view); + + listItemLayout = view.findViewById(R.id.listitem_layout); + mTitle = view.findViewById(R.id.simple_key); + } + + LinearLayout listItemLayout; + TextView mTitle; + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int index) { + + if (viewHolder instanceof ItemViewHolder) { + SimpleKeyBean searchHistoryBean = list.get(index); + final ItemViewHolder itemViewHold = ((ItemViewHolder) viewHolder); + + if (textFont != null) itemViewHold.mTitle.setTypeface(textFont); + + String text = searchHistoryBean.getText(); + if (text.length() > 300) itemViewHold.mTitle.setText(text.substring(0, 300)); + else itemViewHold.mTitle.setText(text); + + if (textSize > 0) itemViewHold.mTitle.setTextSize(textSize); + + ViewGroup.LayoutParams lp = itemViewHold.listItemLayout.getLayoutParams(); + if (lp instanceof FlexboxLayoutManager.LayoutParams) { + FlexboxLayoutManager.LayoutParams flexboxLp = + (FlexboxLayoutManager.LayoutParams) itemViewHold.listItemLayout.getLayoutParams(); + + itemViewHold.mTitle.setTextColor(textColor); + + int marginTop = flexboxLp.getMarginTop(); + int marginX = flexboxLp.getMarginLeft(); + if (keyMarginTop > 0) marginTop = keyMarginTop; + if (keyMarginX > 0) marginX = keyMarginX; + + flexboxLp.setMargins(marginX, marginTop, marginX, flexboxLp.getMarginBottom()); + + // TODO 设置剪贴板列表样式 + // copy SimpleAdapter 会造成高度始终为 3 行无法自适应的效果。 + + } + + if (background != null) + itemViewHold.listItemLayout.setBackground( + Config.get(myContext) + .getDrawable( + "long_text_back_color", + "key_border", + "key_long_text_border", + "round_corner", + null)); + + // 如果设置了回调,则设置点击事件 + if (mOnItemClickListener != null) { + itemViewHold.listItemLayout.setOnClickListener( + view -> { + int position = itemViewHold.getLayoutPosition(); + mOnItemClickListener.onItemClick(itemViewHold.listItemLayout, position); + }); + } + + itemViewHold.listItemLayout.setOnLongClickListener( + view -> { + int position = itemViewHold.getLayoutPosition(); + // TODO 长按删除、编辑剪贴板 + // 当文本较长时,目前样式只缩略显示为 3 行,长按时 toast 消息可以预览全文,略有用处。 + ToastUtils.showShort(list.get(position).getText()); + return true; + }); + + // TODO 剪贴板列表点击时产生背景变色效果 + itemViewHold.listItemLayout.setOnTouchListener( + (view, motionEvent) -> { + int action = motionEvent.getAction(); + switch (action) { + case MotionEvent.ACTION_DOWN: + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + break; + } + return false; + }); + } + } + + /** 添加 OnItemClickListener 回调 * */ + public interface OnItemClickListener { + void onItemClick(View view, int position); + } + + private OnItemClickListener mOnItemClickListener; + + public void setOnItemClickListener(OnItemClickListener mOnItemClickListener) { + this.mOnItemClickListener = mOnItemClickListener; + } +} diff --git a/app/src/main/java/com/osfans/trime/draft/DraftBean.java b/app/src/main/java/com/osfans/trime/draft/DraftBean.java new file mode 100644 index 0000000000..a7d1e9a126 --- /dev/null +++ b/app/src/main/java/com/osfans/trime/draft/DraftBean.java @@ -0,0 +1,61 @@ +package com.osfans.trime.draft; + +import com.osfans.trime.ime.symbol.SimpleKeyBean; + +public class DraftBean extends SimpleKeyBean { + private long time; + private String text; + private final String html; + private int type; + + public long getTime() { + return time; + } + + public String getHtml() { + return html; + } + + public String getText() { + return text; + } + + public int getType() { + return type; + } + + public void setText(String text) { + this.text = text; + } + + public void setTime(long time) { + this.time = time; + } + + public void setType(int type) { + this.type = type; + } + + public DraftBean(String text) { + this.text = text; + this.time = System.currentTimeMillis(); + this.type = 0; + this.html = ""; + } + + @SuppressWarnings("unused") + public DraftBean(String text, String html) { + this.text = text; + this.time = System.currentTimeMillis(); + this.type = 1; + this.html = html; + } + + @SuppressWarnings("unused") + public DraftBean(String text, String html, int type, long time) { + this.text = text; + this.time = time; + this.type = type; + this.html = html; + } +} diff --git a/app/src/main/java/com/osfans/trime/draft/DraftDao.java b/app/src/main/java/com/osfans/trime/draft/DraftDao.java new file mode 100644 index 0000000000..8f91a03a49 --- /dev/null +++ b/app/src/main/java/com/osfans/trime/draft/DraftDao.java @@ -0,0 +1,92 @@ +package com.osfans.trime.draft; + +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import androidx.annotation.NonNull; +import com.osfans.trime.ime.core.Trime; +import com.osfans.trime.ime.symbol.SimpleKeyBean; +import java.util.ArrayList; +import java.util.List; +import timber.log.Timber; + +public class DraftDao { + + private SQLiteOpenHelper helper; + private static DraftDao self; + + public static DraftDao get() { + if (null == self) self = new DraftDao(); + return self; + } + + public DraftDao() {} + + public void insert(@NonNull DraftBean clipboardBean) { + helper = new DraftlHelper(Trime.getService(), "draft.db", null, 1); + SQLiteDatabase db = helper.getWritableDatabase(); + db.execSQL( + "insert into t_clipboard(text,html,type,time) values(?,?,?,?)", + new Object[] { + clipboardBean.getText(), + clipboardBean.getHtml(), + clipboardBean.getType(), + clipboardBean.getTime() + }); + db.close(); + } + + /** 删除文字相同的剪贴板记录,插入新记录 * */ + public void add(@NonNull DraftBean clipboardBean) { + helper = new DraftlHelper(Trime.getService(), "draft.db", null, 1); + SQLiteDatabase db = helper.getWritableDatabase(); + db.delete("t_clipboard", "text=?", new String[] {clipboardBean.getText()}); + db.execSQL( + "insert into t_clipboard(text,html,type,time) values(?,?,?,?)", + new Object[] { + clipboardBean.getText(), + clipboardBean.getHtml(), + clipboardBean.getType(), + clipboardBean.getTime() + }); + db.close(); + } + + public void update(@NonNull DraftBean clipboardBean) { + helper = new DraftlHelper(Trime.getService(), "draft.db", null, 1); + SQLiteDatabase db = helper.getWritableDatabase(); + db.execSQL( + "insert into t_clipboard(text,html,type,time) values(?,?,?,?)", + new Object[] { + clipboardBean.getText(), + clipboardBean.getHtml(), + clipboardBean.getType(), + clipboardBean.getTime() + }); + db.close(); + } + + public List getAllSimpleBean(int size) { + + List list = new ArrayList<>(); + if (size == 0) return list; + + String sql = "select text , html , type , time from t_clipboard ORDER BY time DESC"; + if (size > 0) sql = sql + " limit 0," + size; + + helper = new DraftlHelper(Trime.getService(), "draft.db", null, 1); + + SQLiteDatabase db = helper.getWritableDatabase(); + Cursor cursor = db.rawQuery(sql, null); + if (cursor != null) { + while (cursor.moveToNext()) { + DraftBean v = new DraftBean(cursor.getString(0)); + list.add(v); + } + cursor.close(); + } + db.close(); + Timber.d("getAllSimpleBean() size=%s limit=%s", list.size(), size); + return list; + } +} diff --git a/app/src/main/java/com/osfans/trime/draft/DraftlHelper.java b/app/src/main/java/com/osfans/trime/draft/DraftlHelper.java new file mode 100644 index 0000000000..fdd38df331 --- /dev/null +++ b/app/src/main/java/com/osfans/trime/draft/DraftlHelper.java @@ -0,0 +1,34 @@ +package com.osfans.trime.draft; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import androidx.annotation.NonNull; +import timber.log.Timber; + +public class DraftlHelper extends SQLiteOpenHelper { + + public static final String CREATE_STUDENT = + "create table t_clipboard (" + + "id integer primary key, text TEXT, html TEXT, type integer, time integer)"; + + public DraftlHelper( + Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { + super(context, name, factory, version); + } + + @Override + public void onOpen(SQLiteDatabase db) { + Timber.i("open db"); + super.onOpen(db); + } + + @Override + public void onCreate(@NonNull SQLiteDatabase db) { + Timber.i("create db"); + db.execSQL(CREATE_STUDENT); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {} +} diff --git a/app/src/main/java/com/osfans/trime/ime/core/EditorInstance.kt b/app/src/main/java/com/osfans/trime/ime/core/EditorInstance.kt index e283aa3794..d1efa29169 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/EditorInstance.kt +++ b/app/src/main/java/com/osfans/trime/ime/core/EditorInstance.kt @@ -6,10 +6,12 @@ import android.view.InputDevice import android.view.KeyCharacterMap import android.view.KeyEvent import android.view.inputmethod.EditorInfo +import android.view.inputmethod.ExtractedTextRequest import android.view.inputmethod.InputConnection import com.osfans.trime.Rime import com.osfans.trime.ime.enums.InlineModeType import com.osfans.trime.ime.text.TextInputManager +import timber.log.Timber class EditorInstance(private val ims: InputMethodService) { @@ -30,6 +32,7 @@ class EditorInstance(private val ims: InputMethodService) { get() = (ims as Trime).textInputManager var lastCommittedText: CharSequence = "" + var draftCache: String = "" fun commitText(text: CharSequence, dispatchToRime: Boolean = true): Boolean { val ic = inputConnection ?: return false @@ -37,6 +40,7 @@ class EditorInstance(private val ims: InputMethodService) { lastCommittedText = text // Fix pressing Delete key will clear the input box issue on BlackBerry ic.clearMetaKeyStates(KeyEvent.getModifierMetaStateMask()) + cacheDraft() return true } @@ -65,6 +69,22 @@ class EditorInstance(private val ims: InputMethodService) { } } + fun cacheDraft(): String { + if (prefs.other.draftLimit.equals("0") || inputConnection == null) + return "" + val et = inputConnection!!.getExtractedText(ExtractedTextRequest(), 0) + if (et == null) { + Timber.e("cacheDraft() et==null") + return "" + } + val cs = et.text ?: return "" + if (cs.isNullOrBlank()) + return "" + draftCache = cs as String + Timber.d("cacheDraft() $draftCache") + return draftCache + } + /** * Gets [n] characters after the cursor's current position. The resulting string may be any * length ranging from 0 to n. diff --git a/app/src/main/java/com/osfans/trime/ime/core/Preferences.kt b/app/src/main/java/com/osfans/trime/ime/core/Preferences.kt index dba11bc143..74cacdb1de 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/Preferences.kt +++ b/app/src/main/java/com/osfans/trime/ime/core/Preferences.kt @@ -299,7 +299,9 @@ class Preferences( const val CLICK_CANDIDATE_AND_COMMIT = "other__click_candidate_and_commit" const val CLIPBOARD_COMPARE_RULES = "other__clipboard_compare" const val CLIPBOARD_OUTPUT_RULES = "other__clipboard_output" - const val CLIPBOARD_MANAGER_RULES = "other__clipboard_manager" + const val DRAFT_OUTPUT_RULES = "other__draft_output" + const val DRAFT_LIMIT = "other__draft_limit" + const val CLIPBOARD_LIMIT = "other__clipboard_limit" } var uiMode: String get() = prefs.getPref(UI_MODE, "auto") @@ -325,8 +327,14 @@ class Preferences( var clipboardOutputRules: String get() = prefs.getPref(CLIPBOARD_OUTPUT_RULES, "") set(v) = prefs.setPref(CLIPBOARD_OUTPUT_RULES, v) - var clipboardManagerRules: String - get() = prefs.getPref(CLIPBOARD_MANAGER_RULES, "0") - set(v) = prefs.setPref(CLIPBOARD_MANAGER_RULES, v) + var draftOutputRules: String + get() = prefs.getPref(DRAFT_OUTPUT_RULES, "") + set(v) = prefs.setPref(DRAFT_OUTPUT_RULES, v) + var clipboardLimit: String + get() = prefs.getPref(CLIPBOARD_LIMIT, "50") + set(v) = prefs.setPref(CLIPBOARD_LIMIT, v) + var draftLimit: String + get() = prefs.getPref(DRAFT_LIMIT, "20") + set(v) = prefs.setPref(DRAFT_LIMIT, v) } } diff --git a/app/src/main/java/com/osfans/trime/ime/core/Trime.java b/app/src/main/java/com/osfans/trime/ime/core/Trime.java index ea9847b727..e94b04553f 100644 --- a/app/src/main/java/com/osfans/trime/ime/core/Trime.java +++ b/app/src/main/java/com/osfans/trime/ime/core/Trime.java @@ -60,6 +60,7 @@ import com.osfans.trime.common.ViewUtils; import com.osfans.trime.databinding.CompositionRootBinding; import com.osfans.trime.databinding.InputRootBinding; +import com.osfans.trime.draft.DraftDao; import com.osfans.trime.ime.enums.WindowsPositionType; import com.osfans.trime.ime.keyboard.Event; import com.osfans.trime.ime.keyboard.InputFeedbackManager; @@ -360,8 +361,11 @@ public void onCreate() { keyboardSwitcher = new KeyboardSwitcher(); - liquidKeyboard = new LiquidKeyboard(this, getImeConfig().getClipboardMaxSize()); + liquidKeyboard = + new LiquidKeyboard( + this, getImeConfig().getClipboardLimit(), getImeConfig().getDraftLimit()); clipBoardMonitor(); + DraftDao.get(); } catch (Exception e) { super.onCreate(); e.fillInStackTrace(); @@ -418,7 +422,7 @@ public void invalidate() { } private void hideCompositionView() { - if (isPopupWindowMovable.equals("once")) { + if (isPopupWindowMovable != null && isPopupWindowMovable.equals("once")) { popupWindowPos = getImeConfig().getWinPos(); } @@ -693,10 +697,13 @@ public void onStartInputView(EditorInfo attribute, boolean restarting) { bindKeyboardToInputView(); if (!restarting) setNavBarColor(); setCandidatesViewShown(!Rime.isEmpty()); // 軟鍵盤出現時顯示候選欄 + activeEditorInstance.cacheDraft(); + addDraft(); } @Override public void onFinishInputView(boolean finishingInput) { + addDraft(); super.onFinishInputView(finishingInput); // Dismiss any pop-ups when the input-view is being finished and hidden. mainKeyboardView.closing(); @@ -1013,6 +1020,7 @@ public void showOptionsDialog() { */ private boolean performEnter(int keyCode) { // 回車 if (keyCode == KeyEvent.KEYCODE_ENTER) { + activeEditorInstance.cacheDraft(); if (textInputManager.getPerformEnterAsLineBreak()) { commitText("\n"); } else { @@ -1141,8 +1149,9 @@ private void clipBoardMonitor() { final Config imeConfig = getImeConfig(); clipBoard.addPrimaryClipChangedListener( () -> { - if (imeConfig.getClipboardMaxSize() != 0) { + if (imeConfig.getClipboardLimit() != 0) { final ClipData clipData = clipBoard.getPrimaryClip(); + if (clipData == null) return; final ClipData.Item item = clipData.getItemAt(0); if (item == null) return; @@ -1158,4 +1167,19 @@ private void clipBoardMonitor() { } }); } + + private String draftString = "", draftCache = ""; + + private void addDraft() { + draftCache = activeEditorInstance.getDraftCache(); + if (draftCache.isEmpty() || draftCache.trim().equals(draftString)) return; + + if (getImeConfig().getDraftLimit() != 0) { + Timber.i("addDraft() cache=%s, string=%s", draftString, draftCache); + if (StringUtils.mismatch(draftCache, getImeConfig().getDraftOutput())) { + draftString = draftCache.trim(); + liquidKeyboard.addDraftData(draftCache); + } + } + } } diff --git a/app/src/main/java/com/osfans/trime/ime/enums/SymbolKeyboardType.kt b/app/src/main/java/com/osfans/trime/ime/enums/SymbolKeyboardType.kt index 705cf01735..c4d18de56f 100644 --- a/app/src/main/java/com/osfans/trime/ime/enums/SymbolKeyboardType.kt +++ b/app/src/main/java/com/osfans/trime/ime/enums/SymbolKeyboardType.kt @@ -10,6 +10,9 @@ enum class SymbolKeyboardType { // 剪贴板(大段文本自动缩略,按键长度自适应。) CLIPBOARD, + // 文本框编辑历史,即“草稿箱” + DRAFT, + // 近期上屏符号历史(需要区分来源并提示?) HISTORY, diff --git a/app/src/main/java/com/osfans/trime/ime/keyboard/InputFeedbackManager.kt b/app/src/main/java/com/osfans/trime/ime/keyboard/InputFeedbackManager.kt index 0d5ffb4c07..a0c89b5b67 100644 --- a/app/src/main/java/com/osfans/trime/ime/keyboard/InputFeedbackManager.kt +++ b/app/src/main/java/com/osfans/trime/ime/keyboard/InputFeedbackManager.kt @@ -29,7 +29,7 @@ class InputFeedbackManager( try { vibrator = ims.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator audioManager = ims.getSystemService(Context.AUDIO_SERVICE) as? AudioManager - tts = TextToSpeech(ims) { } + tts = TextToSpeech(ims.applicationContext) { } } catch (e: Exception) { e.printStackTrace() } diff --git a/app/src/main/java/com/osfans/trime/ime/symbol/LiquidKeyboard.java b/app/src/main/java/com/osfans/trime/ime/symbol/LiquidKeyboard.java index 2315beb11e..4f0f1d2e98 100644 --- a/app/src/main/java/com/osfans/trime/ime/symbol/LiquidKeyboard.java +++ b/app/src/main/java/com/osfans/trime/ime/symbol/LiquidKeyboard.java @@ -13,6 +13,9 @@ import com.osfans.trime.clipboard.ClipboardAdapter; import com.osfans.trime.clipboard.ClipboardBean; import com.osfans.trime.clipboard.ClipboardDao; +import com.osfans.trime.draft.DraftAdapter; +import com.osfans.trime.draft.DraftBean; +import com.osfans.trime.draft.DraftDao; import com.osfans.trime.ime.core.Trime; import com.osfans.trime.ime.enums.SymbolKeyboardType; import com.osfans.trime.setup.Config; @@ -29,11 +32,12 @@ public class LiquidKeyboard { private RecyclerView keyboardView; private LinearLayout parentView; private ClipboardAdapter mClipboardAdapter; + private DraftAdapter mDraftAdapter; private SimpleAdapter simpleAdapter; - private List clipboardBeanList; + private List clipboardBeanList, draftBeanList; private final List simpleKeyBeans; private List historyBeans; - private int margin_x, margin_top, single_width, parent_width, clipboard_max_size; + private int margin_x, margin_top, single_width, parent_width, clipboard_max_size, draft_max_size; private int keyHeight; private boolean isLand; @@ -48,13 +52,17 @@ public void setLand(boolean land) { isLand = land; } - public LiquidKeyboard(Context context, int clipboard_max_size) { + public LiquidKeyboard(Context context, int clipboard_max_size, int draft_max_size) { this.context = context; this.clipboard_max_size = clipboard_max_size; + this.draft_max_size = draft_max_size; clipboardBeanList = ClipboardDao.get().getAllSimpleBean(clipboard_max_size); Timber.d("clipboardBeanList.size=%s", clipboardBeanList.size()); + draftBeanList = DraftDao.get().getAllSimpleBean(draft_max_size); + Timber.d("draftBeanList.size=%s", draftBeanList.size()); + simpleKeyBeans = new ArrayList<>(); historySavePath = context.getExternalFilesDir(null).getAbsolutePath() + File.separator + "key_history"; @@ -67,6 +75,13 @@ public void addClipboardData(String text) { if (mClipboardAdapter != null) mClipboardAdapter.notifyItemInserted(0); } + public void addDraftData(String text) { + DraftBean bean = new DraftBean(text); + DraftDao.get().add(bean); + draftBeanList.add(0, bean); + if (mDraftAdapter != null) mDraftAdapter.notifyItemInserted(0); + } + public void select(int i) { TabTag tag = TabManager.getTag(i); calcPadding(tag.type); @@ -76,6 +91,10 @@ public void select(int i) { TabManager.get().select(i); initClipboardData(); break; + case DRAFT: + TabManager.get().select(i); + initDraftData(); + break; case HISTORY: TabManager.get().select(i); default: @@ -123,7 +142,7 @@ public void calcPadding(int width) { if (single_width <= 0) single_width = context.getResources().getDimensionPixelSize(R.dimen.simple_key_single_width); - clipboard_max_size = config.getClipboardMaxSize(); + clipboard_max_size = config.getClipboardLimit(); } // 每次点击tab都需要刷新的参数 @@ -146,6 +165,7 @@ private void calcPadding(SymbolKeyboardType type) { public void initFixData(int i) { keyboardView.removeAllViews(); mClipboardAdapter = null; + mDraftAdapter = null; // 设置布局管理器 FlexboxLayoutManager flexboxLayoutManager = new FlexboxLayoutManager(context); // flexDirection 属性决定主轴的方向(即项目的排列方向)。类似 LinearLayout 的 vertical 和 horizontal。 @@ -210,7 +230,8 @@ public void initClipboardData() { // flexboxLayoutManager.setAlignItems(AlignItems.BASELINE); keyboardView.setLayoutManager(flexboxLayoutManager); - clipboard_max_size = Config.get(context).getClipboardMaxSize(); + clipboard_max_size = Config.get(context).getClipboardLimit(); + clipboardBeanList = ClipboardDao.get().getAllSimpleBean(clipboard_max_size); mClipboardAdapter = new ClipboardAdapter(context, clipboardBeanList); @@ -228,4 +249,39 @@ public void initClipboardData() { } }); } + + public void initDraftData() { + keyboardView.removeAllViews(); + simpleAdapter = null; + + // 设置布局管理器 + FlexboxLayoutManager flexboxLayoutManager = new FlexboxLayoutManager(context); + // flexDirection 属性决定主轴的方向(即项目的排列方向)。类似 LinearLayout 的 vertical 和 horizontal。 + flexboxLayoutManager.setFlexDirection(FlexDirection.ROW); // 主轴为水平方向,起点在左端。 + // flexWrap 默认情况下 Flex 跟 LinearLayout 一样,都是不带换行排列的,但是flexWrap属性可以支持换行排列。 + flexboxLayoutManager.setFlexWrap(FlexWrap.WRAP); // 按正常方向换行 + // justifyContent 属性定义了项目在主轴上的对齐方式。 + flexboxLayoutManager.setJustifyContent(JustifyContent.FLEX_START); // 交叉轴的起点对齐。 + // flexboxLayoutManager.setAlignItems(AlignItems.BASELINE); + keyboardView.setLayoutManager(flexboxLayoutManager); + + draft_max_size = Config.get(context).getDraftLimit(); + + draftBeanList = DraftDao.get().getAllSimpleBean(draft_max_size); + mDraftAdapter = new DraftAdapter(context, draftBeanList); + + mDraftAdapter.configStyle(margin_x, margin_top); + + keyboardView.setAdapter(mDraftAdapter); + // 调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局 + keyboardView.setSelected(true); + + mDraftAdapter.setOnItemClickListener( + (view, position) -> { + InputConnection ic = Trime.getService().getCurrentInputConnection(); + if (ic != null) { + ic.commitText(draftBeanList.get(position).getText(), 1); + } + }); + } } diff --git a/app/src/main/java/com/osfans/trime/settings/fragments/OtherFragment.kt b/app/src/main/java/com/osfans/trime/settings/fragments/OtherFragment.kt index b7a1938bbb..2b38d2dd42 100644 --- a/app/src/main/java/com/osfans/trime/settings/fragments/OtherFragment.kt +++ b/app/src/main/java/com/osfans/trime/settings/fragments/OtherFragment.kt @@ -61,6 +61,11 @@ class OtherFragment : sharedPreferences?.getString(key, "") ) } + "other__draft_output" -> { + Config.get(context).setDraftOutput( + sharedPreferences?.getString(key, "") + ) + } } } diff --git a/app/src/main/java/com/osfans/trime/setup/Config.java b/app/src/main/java/com/osfans/trime/setup/Config.java index e7ca9391bb..094f28bedd 100644 --- a/app/src/main/java/com/osfans/trime/setup/Config.java +++ b/app/src/main/java/com/osfans/trime/setup/Config.java @@ -91,7 +91,7 @@ public static Config get(Context context) { private static final Pattern pattern = Pattern.compile("\\s*\n\\s*"); - private String[] clipBoardCompare, clipBoardOutput; + private String[] clipBoardCompare, clipBoardOutput, draftOutput; @NonNull private Preferences getPrefs() { @@ -107,12 +107,9 @@ public Config(@NonNull Context context) { deployTheme(); init(); setSoundFromColor(); - prepareCLipBoardRule(); - } - - private void prepareCLipBoardRule() { clipBoardCompare = getPrefs().getOther().getClipboardCompareRules().trim().split("\n"); clipBoardOutput = getPrefs().getOther().getClipboardOutputRules().trim().split("\n"); + draftOutput = getPrefs().getOther().getDraftOutputRules().trim().split("\n"); } public String[] getClipBoardCompare() { @@ -123,8 +120,16 @@ public String[] getClipBoardOutput() { return clipBoardOutput; } - public int getClipboardMaxSize() { - return Integer.parseInt(getPrefs().getOther().getClipboardManagerRules()); + public String[] getDraftOutput() { + return draftOutput; + } + + public int getClipboardLimit() { + return Integer.parseInt(getPrefs().getOther().getClipboardLimit()); + } + + public int getDraftLimit() { + return Integer.parseInt(getPrefs().getOther().getDraftLimit()); } public void setClipBoardCompare(String str) { @@ -141,6 +146,13 @@ public void setClipBoardOutput(String str) { getPrefs().getOther().setClipboardOutputRules(s); } + public void setDraftOutput(String str) { + String s = pattern.matcher(str).replaceAll("\n").trim(); + draftOutput = s.split("\n"); + + getPrefs().getOther().setDraftOutputRules(s); + } + public String getTheme() { return themeName; } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b3a7056b1e..3998eabc39 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -165,4 +165,6 @@ 同文输入法需要允许显示在其他应用上方以能显示应用外悬浮窗或对话框,点击“确定”以授权。 按键音效包 正在应用音效... + 草稿箱管理器 + 草稿箱过滤规则\n每行为一条正则,与规则匹配的文本不会被缓存 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 1bdea7dbc3..79e1c0ebef 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -166,4 +166,6 @@ 同文輸入法需要允許顯示在其他應用上方以能顯示應用外懸浮窗或對話方塊,點選“確定”以授權。 按鍵音效包 正在應用音效... + 草稿箱管理器 + 草稿箱過濾規則\n每行爲一條正則表達式,與規則匹配的內容不會被緩存 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 01a934c499..2a99e50f29 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -166,4 +166,6 @@ TRIME need alert window permission to show up popup window or dialog, tap OK to grant the permission. Key Sound Package Loading sound... + Draft Manager + Draft Filter diff --git a/app/src/main/res/xml/other_preference.xml b/app/src/main/res/xml/other_preference.xml index 86c6b4182f..56ca2524fd 100644 --- a/app/src/main/res/xml/other_preference.xml +++ b/app/src/main/res/xml/other_preference.xml @@ -33,12 +33,14 @@ app:iconSpaceReserved="false" android:title="@string/other__click_candidate_and_commit" /> + + @@ -52,4 +54,19 @@ android:title="@string/other__clipboard_output_title" android:defaultValue="" app:useSimpleSummaryProvider="true"/> + + + + + \ No newline at end of file