/* * Copyright (C) 2015-present, osfans * waxaca@163.com https://github.com/osfans * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.osfans.trime; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Logger; /** * Rime與OpenCC的Java實現 * * @see Rime OpenCC */ public class Rime { /** Rime編碼區 */ public static class RimeComposition { int length; int cursor_pos; int sel_start; int sel_end; String preedit; byte[] bytes; public String getText() { if (length == 0) return ""; bytes = preedit.getBytes(); return preedit; } public int getStart() { if (length == 0) return 0; return new String(bytes, 0, sel_start).length(); } public int getEnd() { if (length == 0) return 0; return new String(bytes, 0, sel_end).length(); } } /** Rime候選項 */ public static class RimeCandidate { String text; String comment; } /** Rime候選區,包含多個{@link RimeCandidate 候選項} */ public static class RimeMenu { int page_size; int page_no; boolean is_last_page; int highlighted_candidate_index; int num_candidates; RimeCandidate[] candidates; String select_keys; } /** Rime上屏的字符串 */ public static class RimeCommit { int data_size; // v0.9 String text; } /** Rime環境,包括 {@link RimeComposition 編碼區} 、{@link RimeMenu 候選區} */ public static class RimeContext { int data_size; // v0.9 RimeComposition composition; RimeMenu menu; // v0.9.2 String commit_text_preview; String[] select_labels; public int size() { if (menu == null) return 0; return menu.num_candidates; } public RimeCandidate[] getCandidates() { return size() == 0 ? null : menu.candidates; } } /** Rime狀態 */ public static class RimeStatus { int data_size; // v0.9 String schema_id; String schema_name; boolean is_disabled; boolean is_composing; boolean is_ascii_mode; boolean is_full_shape; boolean is_simplified; boolean is_traditional; boolean is_ascii_punct; } /** Rime方案 */ public static class RimeSchema { private String kRightArrow = "→ "; private String kRadioSelected = " ✓"; Map schema = new HashMap(); List> switches = new ArrayList>(); public RimeSchema(String schema_id) { Object o; o = schema_get_value(schema_id, "schema"); if (o == null || !(o instanceof Map)) return; schema = (Map) o; o = schema_get_value(schema_id, "switches"); if (o == null || !(o instanceof List)) return; switches = (List>) o; check(); //檢查不在選單中顯示的選項 } public void check() { if (switches.isEmpty()) return; for (Iterator it = switches.iterator(); it.hasNext(); ) { Map o = (Map) it.next(); if (!o.containsKey("states")) it.remove(); } } public RimeCandidate[] getCandidates() { if (switches.isEmpty()) return null; RimeCandidate[] candidates = new RimeCandidate[switches.size()]; int i = 0; for (Map o : switches) { candidates[i] = new RimeCandidate(); List states = (List) o.get("states"); for (int j = 0; j < states.size(); j++) { if ("西文".equals(states.get(j).toString())) { states.set(j, "英文"); } if (1==1) { states.set(j, ""); } } Integer value = (Integer) o.get("value"); if (value == null) value = 0; candidates[i].text = states.get(value).toString(); // System.out.println("RimeSchema.getCandidates "+candidates[i].text); candidates[i].comment = o.containsKey("options") ? "" : kRightArrow + states.get(1 - value).toString(); i++; } return candidates; } public void getValue() { if (switches.isEmpty()) return; //無方案 for (int j = 0; j < switches.size(); j++) { Map o = switches.get(j); if (o.containsKey("options")) { List options = (List) o.get("options"); for (int i = 0; i < options.size(); i++) { String s = options.get(i); if (Rime.get_option(s)) { o.put("value", i); break; } } } else { o.put("value", Rime.get_option(o.get("name").toString()) ? 1 : 0); } switches.set(j, o); } } public void toggleOption(int i) { if (switches.isEmpty()) return; Map o = switches.get(i); Integer value = (Integer) o.get("value"); if (value == null) value = 0; if (o.containsKey("options")) { List options = (List) o.get("options"); Rime.setOption(options.get(value), false); value = (value + 1) % options.size(); Rime.setOption(options.get(value), true); } else { value = 1 - value; Rime.setOption(o.get("name").toString(), value == 1); } o.put("value", value); switches.set(i, o); } } private static Rime self; private static Logger Log = Logger.getLogger(Rime.class.getSimpleName()); private static RimeCommit mCommit = new RimeCommit(); private static RimeContext mContext = new RimeContext(); private static RimeStatus mStatus = new RimeStatus(); private static RimeSchema mSchema; private static List mSchemaList; private static boolean mOnMessage; static { System.loadLibrary("opencc"); System.loadLibrary("rime"); System.loadLibrary("rime_jni"); } public static int META_SHIFT_ON = get_modifier_by_name("Shift"); public static int META_CTRL_ON = get_modifier_by_name("Control"); public static int META_ALT_ON = get_modifier_by_name("Alt"); public static int META_RELEASE_ON = get_modifier_by_name("Release"); private static boolean showSwitches = true; public static void setShowSwitches(boolean show) { showSwitches = show; } public static boolean hasMenu() { return isComposing() && mContext.menu.num_candidates != 0; } public static boolean hasLeft() { return hasMenu() && mContext.menu.page_no != 0; } public static boolean hasRight() { return hasMenu() && !mContext.menu.is_last_page; } public static boolean isPaging() { return hasLeft(); } public static boolean isComposing() { return mStatus.is_composing; } public static boolean isAsciiMode() { return mStatus.is_ascii_mode; } public static RimeComposition getComposition() { if (mContext == null) return null; return mContext.composition; } public static String getCompositionText() { RimeComposition composition = getComposition(); return (composition == null) ? "" : composition.preedit; } public static String getComposingText() { if (mContext == null || mContext.commit_text_preview == null) return ""; return mContext.commit_text_preview; } private Rime(boolean full_check) { init(full_check); self = this; } private static void initSchema() { mSchemaList = get_schema_list(); String schema_id = getSchemaId(); mSchema = new RimeSchema(schema_id); getStatus(); } private static boolean getStatus() { mSchema.getValue(); return get_status(mStatus); } private static void init(boolean full_check) { mOnMessage = false; initialize(Config.get().getSharedDataDir(), Config.get().getUserDataDir()); check(full_check); set_notification_handler(); if (!find_session()) { if (create_session() == 0) { Log.severe("Error creating rime session"); return; } } initSchema(); } public static void destroy() { destroy_session(); finalize1(); self = null; } public static String getCommitText() { return mCommit.text; } public static boolean getCommit() { return get_commit(mCommit); } private static boolean getContexts() { boolean b = get_context(mContext); getStatus(); return b; } public static boolean isVoidKeycode(int keycode) { int XK_VoidSymbol = 0xffffff; return keycode <= 0 || keycode == XK_VoidSymbol; } private static boolean onKey(int keycode, int mask) { if (isVoidKeycode(keycode)) return false; boolean b = process_key(keycode, mask); Log.info("b=" + b + ",keycode=" + keycode + ",mask=" + mask); getContexts(); return b; } public static boolean onKey(int[] event) { if (event != null && event.length == 2) return onKey(event[0], event[1]); return false; } public static boolean isValidText(CharSequence text) { if (text == null || text.length() == 0) return false; int ch = text.toString().codePointAt(0); return ch >= 0x20 && ch < 0x80; } public static boolean onText(CharSequence text) { if (!isValidText(text)) return false; boolean b = simulate_key_sequence(text.toString().replace("{}", "{braceleft}{braceright}")); Log.info("b=" + b + ",input=" + text); getContexts(); return b; } public static RimeCandidate[] getCandidates() { if (!isComposing() && showSwitches) return mSchema.getCandidates(); return mContext.getCandidates(); } public static String[] getSelectLabels() { if (mContext != null && mContext.size() > 0) { if (mContext.select_labels != null) return mContext.select_labels; if (mContext.menu.select_keys != null) return mContext.menu.select_keys.split("\\B"); int n = mContext.size(); String[] labels = new String[n]; for (int i = 0; i < n; i++) { labels[i] = String.valueOf((i + 1) % 10); } return labels; } return null; } public static int getCandHighlightIndex() { return isComposing() ? mContext.menu.highlighted_candidate_index : -1; } public static boolean commitComposition() { boolean b = commit_composition(); getContexts(); return b; } public static void clearComposition() { clear_composition(); getContexts(); } public static boolean selectCandidate(int index) { boolean b = select_candidate_on_current_page(index); getContexts(); return b; } public static void setOption(String option, boolean value) { if (mOnMessage) return; set_option(option, value); } public static boolean getOption(String option) { return get_option(option); } public static void toggleOption(String option) { boolean b = getOption(option); setOption(option, !b); } public static void toggleOption(int i) { mSchema.toggleOption(i); } public static void setProperty(String prop, String value) { if (mOnMessage) return; set_property(prop, value); } public static String getProperty(String prop) { return get_property(prop); } public static String getSchemaId() { return get_current_schema(); } private static boolean isEmpty(String s) { return s.contentEquals(".default"); //無方案 } public static boolean isEmpty() { return isEmpty(getSchemaId()); } public static String[] getSchemaNames() { int n = mSchemaList.size(); String[] names = new String[n]; int i = 0; for (Object o : mSchemaList) { Map m = (Map) o; names[i++] = m.get("name"); } return names; } public static int getSchemaIndex() { String schema_id = getSchemaId(); int i = 0; for (Object o : mSchemaList) { Map m = (Map) o; if (m.get("schema_id").contentEquals(schema_id)) return i; i++; } return 0; } public static String getSchemaName() { return mStatus.schema_name; } private static boolean selectSchema(String schema_id) { boolean b = select_schema(schema_id); getContexts(); return b; } public static boolean selectSchema(int id) { int n = mSchemaList.size(); if (id < 0 || id >= n) return false; String schema_id = getSchemaId(); Map m = (Map) mSchemaList.get(id); String target = m.get("schema_id"); if (target.contentEquals(schema_id)) return false; return selectSchema(target); } public static Rime get(boolean full_check) { if (self == null) { if (full_check) Config.deployOpencc(); self = new Rime(full_check); } return self; } public static Rime get() { return get(false); } public static String RimeGetInput() { String s = get_input(); return s == null ? "" : s; } public static int RimeGetCaretPos() { return get_caret_pos(); } public static void RimeSetCaretPos(int caret_pos) { set_caret_pos(caret_pos); getContexts(); } public static void onMessage(String message_type, String message_value) { mOnMessage = true; Log.info(String.format("message: [%s] %s", message_type, message_value)); Trime trime = Trime.getService(); switch (message_type) { case "schema": initSchema(); if (trime != null) { trime.initKeyboard(); trime.updateComposing(); } break; case "option": getStatus(); getContexts(); //切換中英文、簡繁體時更新候選 if (trime != null) { boolean value = !message_value.startsWith("!"); String option = message_value.substring(value ? 0 : 1); trime.onOptionChanged(option, value); } break; } mOnMessage = false; } public static String openccConvert(String line, String name) { if (name != null && name.length() > 0) { File f = new File(Config.get().getResDataDir("opencc"), name); if (f.exists()) return opencc_convert(line, f.getAbsolutePath()); } return line; } public static void check(boolean full_check) { start_maintenance(full_check); if (is_maintenance_mode()) join_maintenance_thread(); } public static boolean syncUserData() { boolean b = sync_user_data(); destroy(); get(); return b; } // init public static native void setup(String shared_data_dir, String user_data_dir); public static native void set_notification_handler(); // entry and exit public static native void initialize(String shared_data_dir, String user_data_dir); public static native void finalize1(); public static native boolean start_maintenance(boolean full_check); public static native boolean is_maintenance_mode(); public static native void join_maintenance_thread(); // deployment public static native void deployer_initialize(String shared_data_dir, String user_data_dir); public static native boolean prebuild(); public static native boolean deploy(); public static native boolean deploy_schema(String schema_file); public static native boolean deploy_config_file(String file_name, String version_key); public static native boolean sync_user_data(); // session management public static native int create_session(); public static native boolean find_session(); public static native boolean destroy_session(); public static native void cleanup_stale_sessions(); public static native void cleanup_all_sessions(); // input public static native boolean process_key(int keycode, int mask); public static native boolean commit_composition(); public static native void clear_composition(); // output public static native boolean get_commit(RimeCommit commit); public static native boolean get_context(RimeContext context); public static native boolean get_status(RimeStatus status); // runtime options public static native void set_option(String option, boolean value); public static native boolean get_option(String option); public static native void set_property(String prop, String value); public static native String get_property(String prop); public static native List get_schema_list(); public static native String get_current_schema(); public static native boolean select_schema(String schema_id); // configuration public static native Boolean config_get_bool(String name, String key); public static native boolean config_set_bool(String name, String key, boolean value); public static native Integer config_get_int(String name, String key); public static native boolean config_set_int(String name, String key, int value); public static native Double config_get_double(String name, String key); public static native boolean config_set_double(String name, String key, double value); public static native String config_get_string(String name, String key); public static native boolean config_set_string(String name, String key, String value); public static native int config_list_size(String name, String key); public static native List config_get_list(String name, String key); public static native Map config_get_map(String name, String key); public static native Object config_get_value(String name, String key); public static native Object schema_get_value(String name, String key); // testing public static native boolean simulate_key_sequence(String key_sequence); public static native String get_input(); public static native int get_caret_pos(); public static native void set_caret_pos(int caret_pos); public static native boolean select_candidate(int index); public static native boolean select_candidate_on_current_page(int index); public static native String get_version(); public static native String get_librime_version(); // module public static native boolean run_task(String task_name); public static native String get_shared_data_dir(); public static native String get_user_data_dir(); public static native String get_sync_dir(); public static native String get_user_id(); // key_table public static native int get_modifier_by_name(String name); public static native int get_keycode_by_name(String name); // customize setting public static native boolean customize_bool(String name, String key, boolean value); public static native boolean customize_int(String name, String key, int value); public static native boolean customize_double(String name, String key, double value); public static native boolean customize_string(String name, String key, String value); public static native List> get_available_schema_list(); public static native List> get_selected_schema_list(); public static native boolean select_schemas(String[] schema_id_list); // opencc public static native String get_opencc_version(); public static native String opencc_convert(String line, String name); public static native void opencc_convert_dictionary( String inputFileName, String outputFileName, String formatFrom, String formatTo); public static native String get_trime_version(); }