Skip to content
This repository was archived by the owner on Jul 22, 2024. It is now read-only.

Commit 861b17c

Browse files
keianhzoMortimerGoro
authored andcommitted
Implement Japanese keyboard (#1311)
* Implement Japanese keyboard #974 * Refactored OpenWnn to a maven artifact (jcenter) * Fixed crash with certain syllable combinations * Fixed Japanese keyboard title Restored column width to the original value
1 parent b646db9 commit 861b17c

File tree

10 files changed

+357
-0
lines changed

10 files changed

+357
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ infer-out/
5656
fastlane/
5757

5858
app/.externalNativeBuild
59+
openwnn/.externalNativeBuild
5960

6061
*.swp
6162

app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ repositories {
332332

333333
dependencies {
334334
implementation fileTree(include: ['*.jar'], dir: 'libs')
335+
implementation deps.openwnn
335336

336337
// Common
337338
// implementation deps.google_vr.sdk_audio

app/src/common/shared/org/mozilla/vrbrowser/input/CustomKeyboard.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ public class CustomKeyboard extends Keyboard {
2626
public static final int KEYCODE_SYMBOLS_CHANGE = -10;
2727
public static final int KEYCODE_VOICE_INPUT = -11;
2828
public static final int KEYCODE_LANGUAGE_CHANGE = -12;
29+
public static final int KEYCODE_EMOJI = -13;
2930

3031
public CustomKeyboard(Context context, int xmlLayoutResId) {
3132
super(context, xmlLayoutResId, 0);
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package org.mozilla.vrbrowser.ui.keyboards;
2+
3+
import android.content.Context;
4+
5+
import androidx.annotation.NonNull;
6+
import androidx.annotation.Nullable;
7+
8+
import org.mozilla.vrbrowser.R;
9+
import org.mozilla.vrbrowser.input.CustomKeyboard;
10+
import org.mozilla.vrbrowser.utils.StringUtils;
11+
12+
import java.util.ArrayList;
13+
import java.util.Arrays;
14+
import java.util.List;
15+
import java.util.Locale;
16+
17+
import jp.co.omronsoft.openwnn.ComposingText;
18+
import jp.co.omronsoft.openwnn.JAJP.OpenWnnEngineJAJP;
19+
import jp.co.omronsoft.openwnn.JAJP.Romkan;
20+
import jp.co.omronsoft.openwnn.LetterConverter;
21+
import jp.co.omronsoft.openwnn.StrSegment;
22+
import jp.co.omronsoft.openwnn.SymbolList;
23+
import jp.co.omronsoft.openwnn.WnnEngine;
24+
import jp.co.omronsoft.openwnn.WnnWord;
25+
26+
public class JapaneseKeyboard extends BaseKeyboard {
27+
28+
private static final String LOGTAG = "VRB";
29+
30+
private CustomKeyboard mKeyboard;
31+
private CustomKeyboard mSymbolsKeyboard;
32+
private List<Character> mAutocompleteEndings = Arrays.asList(
33+
' ', '、', '。','!','?','ー'
34+
);
35+
36+
private SymbolList mSymbolsConverter;
37+
38+
/** OpenWnn dictionary */
39+
private WnnEngine mConverter;
40+
41+
/** Pre-converter (for Romaji-to-Kana input, Hangul input, etc.) */
42+
protected LetterConverter mPreConverter;
43+
44+
/** The inputing/editing string */
45+
protected ComposingText mComposingText;
46+
47+
48+
public JapaneseKeyboard(Context aContext) {
49+
super(aContext);
50+
51+
mConverter = new OpenWnnEngineJAJP();
52+
((OpenWnnEngineJAJP) mConverter).setKeyboardType(OpenWnnEngineJAJP.KEYBOARD_QWERTY);
53+
((OpenWnnEngineJAJP) mConverter).setDictionary(OpenWnnEngineJAJP.DIC_LANG_JP);
54+
mConverter.init();
55+
56+
mPreConverter = new Romkan();
57+
mComposingText = new ComposingText();
58+
}
59+
60+
@NonNull
61+
@Override
62+
public CustomKeyboard getAlphabeticKeyboard() {
63+
if (mKeyboard == null) {
64+
mKeyboard = new CustomKeyboard(mContext.getApplicationContext(), R.xml.keyboard_qwerty_japanese);
65+
}
66+
67+
return mKeyboard;
68+
}
69+
70+
@Nullable
71+
@Override
72+
public CustomKeyboard getSymbolsKeyboard() {
73+
if (mSymbolsKeyboard == null) {
74+
mSymbolsKeyboard = new CustomKeyboard(mContext.getApplicationContext(), R.xml.keyboard_symbols_japanese);
75+
76+
mSymbolsConverter = new SymbolList(mContext, SymbolList.LANG_JA);
77+
}
78+
return mSymbolsKeyboard;
79+
}
80+
81+
@Nullable
82+
@Override
83+
public CandidatesResult getCandidates(String aComposingText) {
84+
if (StringUtils.isEmpty(aComposingText)) {
85+
mComposingText.clear();
86+
return null;
87+
}
88+
89+
// Autocomplete when special characters are clicked
90+
char lastChar = aComposingText.charAt(aComposingText.length() - 1);
91+
boolean autocompose = mAutocompleteEndings.indexOf(lastChar) >= 0;
92+
93+
aComposingText = aComposingText.replaceAll("\\s","");
94+
if (aComposingText.isEmpty()) {
95+
return null;
96+
}
97+
98+
initializeComposingText(aComposingText);
99+
100+
List<Words> words = new ArrayList<>();
101+
int candidates = mConverter.predict(mComposingText, 0, -1);
102+
if (candidates > 0) {
103+
WnnWord word;
104+
while ((word = mConverter.getNextCandidate()) != null) {
105+
words.add(new Words(1, word.stroke, word.candidate));
106+
}
107+
}
108+
109+
CandidatesResult result = new CandidatesResult();
110+
result.words = words;
111+
112+
if (autocompose) {
113+
result.action = CandidatesResult.Action.AUTO_COMPOSE;
114+
result.composing = aComposingText;
115+
116+
mComposingText.clear();
117+
118+
} else {
119+
result.action = CandidatesResult.Action.SHOW_CANDIDATES;
120+
result.composing = mComposingText.toString(ComposingText.LAYER2);
121+
}
122+
123+
return result;
124+
}
125+
126+
@Override
127+
public CandidatesResult getEmojiCandidates(String aComposingText) {
128+
ComposingText text = new ComposingText();
129+
mSymbolsConverter.convert(text);
130+
131+
List<Words> words = new ArrayList<>();
132+
int candidates = mSymbolsConverter.predict(mComposingText, 0, -1);
133+
if (candidates > 0) {
134+
WnnWord word;
135+
while ((word = mSymbolsConverter.getNextCandidate()) != null) {
136+
words.add(new Words(1, word.stroke, word.candidate));
137+
}
138+
}
139+
140+
CandidatesResult result = new CandidatesResult();
141+
result.words = words;
142+
result.action = CandidatesResult.Action.SHOW_CANDIDATES;
143+
result.composing = aComposingText;
144+
145+
return result;
146+
}
147+
148+
@Override
149+
public String getComposingText(String aComposing, String aCode) {
150+
return "";
151+
}
152+
153+
private void initializeComposingText(String text) {
154+
mComposingText.clear();
155+
for (int i=0; i<text.length(); i++) {
156+
mComposingText.insertStrSegment(ComposingText.LAYER0, ComposingText.LAYER1, new StrSegment(text.substring(i, i+1)));
157+
mPreConverter.convert(mComposingText);
158+
}
159+
mComposingText.debugout();
160+
}
161+
162+
@Override
163+
public boolean supportsAutoCompletion() {
164+
return true;
165+
}
166+
167+
@Override
168+
public boolean usesComposingText() {
169+
return true;
170+
}
171+
172+
@Override
173+
public String getKeyboardTitle() {
174+
return StringUtils.getStringByLocale(mContext, R.string.settings_language_japanese, getLocale());
175+
}
176+
177+
@Override
178+
public Locale getLocale() {
179+
return Locale.JAPAN;
180+
}
181+
182+
@Override
183+
public String getSpaceKeyText(String aComposingText) {
184+
if (aComposingText == null || aComposingText.trim().isEmpty()) {
185+
return mContext.getString(R.string.japanese_spacebar_space);
186+
} else {
187+
return mContext.getString(R.string.japanese_spacebar_selection);
188+
}
189+
}
190+
191+
@Override
192+
public String getEnterKeyText(int aIMEOptions, String aComposingText) {
193+
if (aComposingText == null || aComposingText.trim().isEmpty()) {
194+
return super.getEnterKeyText(aIMEOptions, aComposingText);
195+
} else {
196+
return mContext.getString(R.string.japanese_enter_completion);
197+
}
198+
}
199+
200+
@Override
201+
public String getModeChangeKeyText() {
202+
return mContext.getString(R.string.japanese_keyboard_mode_change);
203+
}
204+
205+
@Override
206+
public void clear() {
207+
mConverter.init();
208+
}
209+
210+
}

app/src/common/shared/org/mozilla/vrbrowser/ui/keyboards/KeyboardInterface.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public enum Action {
3434
default @Nullable CandidatesResult getCandidates(String aComposingText) { return null; }
3535
default @Nullable String overrideAddText(String aTextBeforeCursor, String aNextText) { return null; }
3636
default @Nullable String overrideBackspace(String aTextBeforeCursor) { return null; }
37+
default @Nullable CandidatesResult getEmojiCandidates(String aComposingText) { return null; }
3738
default boolean supportsAutoCompletion() { return false; }
3839
default boolean usesComposingText() { return false; }
3940
default boolean usesTextOverride() { return false; }
@@ -43,4 +44,5 @@ public enum Action {
4344
String getSpaceKeyText(String aComposingText);
4445
String getEnterKeyText(int aIMEOptions, String aComposingText);
4546
String getModeChangeKeyText();
47+
default @Nullable void clear() {}
4648
}

app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.mozilla.vrbrowser.ui.keyboards.FrenchKeyboard;
3939
import org.mozilla.vrbrowser.ui.keyboards.GermanKeyboard;
4040
import org.mozilla.vrbrowser.ui.keyboards.ChineseZhuyinKeyboard;
41+
import org.mozilla.vrbrowser.ui.keyboards.JapaneseKeyboard;
4142
import org.mozilla.vrbrowser.ui.keyboards.KeyboardInterface;
4243
import org.mozilla.vrbrowser.ui.keyboards.RussianKeyboard;
4344
import org.mozilla.vrbrowser.ui.keyboards.KoreanKeyboard;
@@ -139,6 +140,7 @@ private void initialize(Context aContext) {
139140
mKeyboards.add(new ChinesePinyinKeyboard(aContext));
140141
mKeyboards.add(new ChineseZhuyinKeyboard(aContext));
141142
mKeyboards.add(new KoreanKeyboard(aContext));
143+
mKeyboards.add(new JapaneseKeyboard(aContext));
142144

143145
mDefaultKeyboardSymbols = new CustomKeyboard(aContext.getApplicationContext(), R.xml.keyboard_symbols);
144146
mKeyboardNumeric = new CustomKeyboard(aContext.getApplicationContext(), R.xml.keyboard_numeric);
@@ -278,6 +280,7 @@ public void updateFocusedView(View aFocusedView) {
278280
mWidgetManager.updateWidget(this);
279281
}
280282

283+
mCurrentKeyboard.clear();
281284
updateCandidates();
282285
updateSpecialKeyLabels();
283286
}
@@ -391,6 +394,9 @@ public void onKey(int primaryCode, int[] keyCodes, boolean hasPopup) {
391394
case CustomKeyboard.KEYCODE_LANGUAGE_CHANGE:
392395
handleGlobeClick();
393396
break;
397+
case CustomKeyboard.KEYCODE_EMOJI:
398+
handleEmojiInput();
399+
break;
394400
case ' ':
395401
handleSpace();
396402
break;
@@ -579,6 +585,12 @@ private void handleGlobeClick() {
579585
mPopupKeyboardLayer.setVisibility(View.VISIBLE);
580586
}
581587

588+
private void handleEmojiInput() {
589+
final KeyboardInterface.CandidatesResult candidates = mCurrentKeyboard.getEmojiCandidates(mComposingText);
590+
setAutoCompletionVisible(candidates != null && candidates.words.size() > 0);
591+
mAutoCompletionView.setItems(candidates != null ? candidates.words : null);
592+
}
593+
582594
private void handleLanguageChange(KeyboardInterface aKeyboard) {
583595
cleanComposingText();
584596

@@ -949,6 +961,7 @@ public void afterTextChanged(Editable aEditable) {
949961
if (!mInternalDeleteHint && mCurrentKeyboard.usesComposingText() && mComposingText.length() > 0 && mTextBefore.length() > 0 && aEditable.toString().length() == 0) {
950962
// Text has been cleared externally (e.g. URLBar text clear button)
951963
mComposingText = "";
964+
mCurrentKeyboard.clear();
952965
updateCandidates();
953966
}
954967
mInternalDeleteHint = false;

app/src/main/res/values/non_L10n.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@
7272
<string name="zhuyin_enter_completion" translatable="false">選定</string>
7373
<string name="zhuyin_keyboard_mode_change" translatable="false">ㄅㄆㄇ</string>
7474

75+
<string name="japanese_spacebar_selection" translatable="false">次変換</string>
76+
<string name="japanese_spacebar_space" translatable="false">空白</string>
77+
<string name="japanese_enter_completion" translatable="false">確定</string>
78+
<string name="japanese_keyboard_mode_change" translatable="false">かな</string>
79+
7580
<!-- Keyboard -->
7681
<string name="keyboard_popup_a" translatable="false">aáàäãåâąæā</string>
7782
<string name="keyboard_popup_b" translatable="false">bƀḃḅḇ</string>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:horizontalGap="@dimen/keyboard_horizontal_gap"
4+
android:verticalGap="@dimen/keyboard_vertical_gap"
5+
android:keyWidth="@dimen/keyboard_key_width"
6+
android:keyHeight="@dimen/keyboard_key_height">
7+
<Row>
8+
<Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left" />
9+
<Key android:codes="119" android:keyLabel="w" />
10+
<Key android:codes="101" android:keyLabel="e" />
11+
<Key android:codes="114" android:keyLabel="r" />
12+
<Key android:codes="116" android:keyLabel="t" />
13+
<Key android:codes="121" android:keyLabel="y" />
14+
<Key android:codes="117" android:keyLabel="u" />
15+
<Key android:codes="105" android:keyLabel="i" />
16+
<Key android:codes="111" android:keyLabel="o" />
17+
<Key android:codes="112" android:keyLabel="p" />
18+
<Key android:codes="95" android:keyLabel="_"/>
19+
<Key android:codes="-5" android:keyIcon="@drawable/ic_icon_keyboard_backspace" android:isRepeatable="true" android:keyWidth="@dimen/keyboard_key_backspace_width" />
20+
</Row>
21+
22+
<Row>
23+
<Key android:codes="97" android:keyLabel="a" android:keyEdgeFlags="left" android:horizontalGap="@dimen/keyboard_left_margin" />
24+
<Key android:codes="115" android:keyLabel="s" />
25+
<Key android:codes="100" android:keyLabel="d" />
26+
<Key android:codes="102" android:keyLabel="f" />
27+
<Key android:codes="103" android:keyLabel="g" />
28+
<Key android:codes="104" android:keyLabel="h" />
29+
<Key android:codes="106" android:keyLabel="j" />
30+
<Key android:codes="107" android:keyLabel="k" />
31+
<Key android:codes="108" android:keyLabel="l" />
32+
<Key android:keyOutputText="" android:keyLabel=""/>
33+
<Key android:codes="-4" android:keyLabel="@string/keyboard_enter_label" android:keyWidth="@dimen/keyboard_key_enter_width" />
34+
</Row>
35+
36+
<Row>
37+
<Key android:codes="-1" android:keyIcon="@drawable/ic_icon_keyboard_shift_off" android:keyEdgeFlags="left"/>
38+
<Key android:codes="122" android:keyLabel="z" />
39+
<Key android:codes="120" android:keyLabel="x" />
40+
<Key android:codes="99" android:keyLabel="c" />
41+
<Key android:codes="118" android:keyLabel="v" />
42+
<Key android:codes="98" android:keyLabel="b" />
43+
<Key android:codes="110" android:keyLabel="n" />
44+
<Key android:codes="109" android:keyLabel="m" />
45+
<Key android:codes="45" android:keyLabel="-" />
46+
<Key android:codes="43" android:keyLabel="+" />
47+
<Key android:codes="47" android:keyLabel="/" />
48+
<Key android:codes="-1" android:keyIcon="@drawable/ic_icon_keyboard_shift_off" />
49+
</Row>
50+
51+
<Row>
52+
<Key android:codes="-2" android:keyLabel="@string/keyboard_symbol" android:keyEdgeFlags="left"/>
53+
<Key android:codes="-12" android:keyIcon="@drawable/ic_icon_keyboard_globe" />
54+
<Key android:codes="32" android:keyLabel="" android:keyWidth="@dimen/keyboard_key_space_width" android:isRepeatable="true"/>
55+
<Key android:keyOutputText="" android:keyLabel=""/>
56+
<Key android:keyOutputText="" android:keyLabel=""/>
57+
<Key android:codes="33" android:keyLabel="!" />
58+
<Key android:codes="63" android:keyLabel="\?" />
59+
<Key android:codes="64" android:keyLabel="\@"/>
60+
</Row>
61+
</Keyboard>

0 commit comments

Comments
 (0)