diff options
Diffstat (limited to 'java')
36 files changed, 1068 insertions, 320 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index a1ffe5a93..054c415a4 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -18,7 +18,7 @@ coreApp="true" package="com.android.inputmethod.latin"> - <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" /> + <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index f1253b40c..8ee859bc7 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -483,6 +483,8 @@ <attr name="localeCode" format="string" /> <attr name="languageCode" format="string" /> <attr name="countryCode" format="string" /> + <!-- Enable split keyboard layout. Disabled by default. --> + <attr name="isSplitLayout" format="boolean" /> </declare-styleable> <declare-styleable name="Keyboard_KeyStyle"> @@ -514,6 +516,8 @@ <attr name="elementKeyboard" format="reference"/> <!-- Enable proximity characters correction. Disabled by default. --> <attr name="enableProximityCharsCorrection" format="boolean" /> + <!-- Indicates if the keyboard layout supports being split or not. false by default --> + <attr name="supportsSplitLayout" format="boolean" /> </declare-styleable> <declare-styleable name="KeyboardLayoutSet_Feature"> diff --git a/java/res/values/keyboard-themes.xml b/java/res/values/keyboard-themes.xml index 9d772c4e7..b0bae9647 100644 --- a/java/res/values/keyboard-themes.xml +++ b/java/res/values/keyboard-themes.xml @@ -26,10 +26,10 @@ <item>@string/keyboard_theme_holo_blue</item> </string-array> <!-- An element must be a keyboard theme id of {@link KeyboardTheme#THEME_ID_*}. --> - <string-array name="keyboard_theme_ids" translatable="false"> + <integer-array name="keyboard_theme_ids" translatable="false"> <item>3</item> <item>4</item> <item>2</item> <item>0</item> - </string-array> + </integer-array> </resources> diff --git a/java/res/xml-sw600dp-land/key_space_3kw.xml b/java/res/xml-sw600dp-land/key_space_3kw.xml new file mode 100644 index 000000000..47c4e4809 --- /dev/null +++ b/java/res/xml-sw600dp-land/key_space_3kw.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <!-- TODO: Consolidate the layout specification between protrait and landscape. + Ideally just the keyWidth should be different --> + <switch> + <!-- fa: Perisan + kn: Kannada + ne: Nepali + te: Telugu --> + <case + latin:languageCode="fa|kn|ne|te" + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="7.0%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageCode="fa|kn|ne|te" + latin:languageSwitchKeyEnabled="false" + > + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="14.0%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="14.0%p" /> + </case> + <!-- languageSwitchKeyEnabled="false" --> + <default> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="21.0%p" /> + </default> + </switch> +</merge> diff --git a/java/res/xml-sw600dp-land/row_qwerty4.xml b/java/res/xml-sw600dp-land/row_qwerty4.xml new file mode 100644 index 000000000..0fdb5c6f5 --- /dev/null +++ b/java/res/xml-sw600dp-land/row_qwerty4.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <!-- Split the 4th row for split layouts --> + <case + latin:isSplitLayout="true" + > + <Row + latin:keyWidth="7.0%p" + latin:backgroundType="functional" + > + <Key + latin:keyStyle="toSymbolKeyStyle" /> + <include + latin:keyboardLayout="@xml/key_comma" /> + <!-- Space key. --> + <include + latin:keyboardLayout="@xml/key_space_3kw" + latin:backgroundType="normal" /> + <Spacer + latin:keyWidth="28.0%p" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="21.0%p" /> + <include + latin:keyboardLayout="@xml/key_period" /> + <include + latin:keyboardLayout="@xml/key_emoji" /> + </Row> + </case> + <default> + <Row + latin:keyWidth="9.0%p" + latin:backgroundType="functional" + > + <Key + latin:keyStyle="toSymbolKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/key_comma" /> + <!-- Space key. --> + <include + latin:keyXPos="19.0%p" + latin:keyboardLayout="@xml/key_space_7kw" + latin:backgroundType="normal" /> + <include + latin:keyboardLayout="@xml/key_period" /> + <include + latin:keyboardLayout="@xml/key_emoji" /> + </Row> + </default> + </switch> +</merge> diff --git a/java/res/xml-sw600dp-land/rows_qwerty.xml b/java/res/xml-sw600dp-land/rows_qwerty.xml new file mode 100644 index 000000000..b580dcf6a --- /dev/null +++ b/java/res/xml-sw600dp-land/rows_qwerty.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <include + latin:keyboardLayout="@xml/key_styles_common" /> + <!-- First row --> + <Row> + <switch> + <!-- Split keyboard layout for the first row --> + <case + latin:isSplitLayout="true" + > + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_left5" + latin:keyWidth="7.0%p" /> + <Spacer + latin:keyWidth="20.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_right5" + latin:keyWidth="7.0%p" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the first row --> + <default> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> + </Row> + <!-- Second row --> + <Row> + <switch> + <!-- Split keyboard layout for the second row --> + <case + latin:isSplitLayout="true" + > + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_left5" + latin:keyXPos="4.0%p" + latin:keyWidth="7.0%p" /> + <Spacer + latin:keyWidth="23.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_right4" + latin:keyWidth="7.0%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the second row --> + <default> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2" + latin:keyXPos="4.5%p" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> + </Row> + <!-- Third row --> + <Row> + <switch> + <!-- Split keyboard layout for the third row --> + <case + latin:isSplitLayout="true" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_left4" + latin:keyWidth="7.0%p" /> + <Spacer + latin:keyWidth="17.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_right3" + latin:keyWidth="7.0%p" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="7.0%p" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the third row --> + <default> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3" + latin:keyWidth="9.0%p" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> + </Row> + <!-- Fourth row --> + <include + latin:keyboardLayout="@xml/row_qwerty4" /> +</merge> diff --git a/java/res/xml-sw600dp/key_space_3kw.xml b/java/res/xml-sw600dp/key_space_3kw.xml new file mode 100644 index 000000000..9932d342e --- /dev/null +++ b/java/res/xml-sw600dp/key_space_3kw.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <switch> + <!-- fa: Perisan + kn: Kannada + ne: Nepali + te: Telugu --> + <case + latin:languageCode="fa|kn|ne|te" + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="8.0%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageCode="fa|kn|ne|te" + latin:languageSwitchKeyEnabled="false" + > + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="16.0%p" /> + <Key + latin:keyStyle="zwnjKeyStyle" /> + </case> + <case + latin:languageSwitchKeyEnabled="true" + > + <Key + latin:keyStyle="languageSwitchKeyStyle" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="16.0%p" /> + </case> + <!-- languageSwitchKeyEnabled="false" --> + <default> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="24.0%p" /> + </default> + </switch> +</merge> diff --git a/java/res/xml-sw600dp/row_qwerty4.xml b/java/res/xml-sw600dp/row_qwerty4.xml index ed7150de4..bcfd2cb7f 100644 --- a/java/res/xml-sw600dp/row_qwerty4.xml +++ b/java/res/xml-sw600dp/row_qwerty4.xml @@ -21,23 +21,54 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Row - latin:keyWidth="9.0%p" - latin:backgroundType="functional" - > - <Key - latin:keyStyle="toSymbolKeyStyle" - latin:keyWidth="10.0%p" /> - <include - latin:keyboardLayout="@xml/key_comma" /> - <!-- Space key. --> - <include - latin:keyXPos="19.0%p" - latin:keyboardLayout="@xml/key_space_7kw" - latin:backgroundType="normal" /> - <include - latin:keyboardLayout="@xml/key_period" /> - <include - latin:keyboardLayout="@xml/key_emoji" /> - </Row> + <switch> + <!-- Split the 4th row for split layouts --> + <case + latin:isSplitLayout="true" + > + <Row + latin:keyWidth="8.0%p" + latin:backgroundType="functional" + > + <Key + latin:keyStyle="toSymbolKeyStyle" /> + <include + latin:keyboardLayout="@xml/key_comma" /> + <!-- Space key. --> + <include + latin:keyboardLayout="@xml/key_space_3kw" + latin:backgroundType="normal" /> + <Spacer + latin:keyWidth="20.0%p" /> + <Key + latin:keyStyle="spaceKeyStyle" + latin:keyWidth="24.0%p" /> + <include + latin:keyboardLayout="@xml/key_period" /> + <include + latin:keyboardLayout="@xml/key_emoji" /> + </Row> + </case> + <default> + <Row + latin:keyWidth="9.0%p" + latin:backgroundType="functional" + > + <Key + latin:keyStyle="toSymbolKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/key_comma" /> + <!-- Space key. --> + <include + latin:keyXPos="19.0%p" + latin:keyboardLayout="@xml/key_space_7kw" + latin:backgroundType="normal" /> + <include + latin:keyboardLayout="@xml/key_period" /> + <include + latin:keyboardLayout="@xml/key_emoji" /> + </Row> + </default> + </switch> </merge> diff --git a/java/res/xml-sw600dp/rows_qwerty.xml b/java/res/xml-sw600dp/rows_qwerty.xml index 58ba1d713..51df4b0cc 100644 --- a/java/res/xml-sw600dp/rows_qwerty.xml +++ b/java/res/xml-sw600dp/rows_qwerty.xml @@ -23,39 +23,114 @@ > <include latin:keyboardLayout="@xml/key_styles_common" /> - <Row - latin:keyWidth="9.0%p" - > - <include - latin:keyboardLayout="@xml/rowkeys_qwerty1" /> - <Key - latin:keyStyle="deleteKeyStyle" - latin:keyWidth="fillRight" /> + <!-- TODO: Consolidate the layout specification between protrait and landscape. + Ideally just the keyWidth should be different and the spacer should adjust to fill + the available space. --> + <!-- First row --> + <Row> + <switch> + <!-- Split keyboard layout for the first row --> + <case + latin:isSplitLayout="true" + > + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_left5" + latin:keyWidth="8.0%p" /> + <Spacer + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_right5" + latin:keyWidth="8.0%p" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the first row --> + <default> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="deleteKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> </Row> - <Row - latin:keyWidth="9.0%p" - > - <include - latin:keyboardLayout="@xml/rowkeys_qwerty2" - latin:keyXPos="4.5%p" /> - <Key - latin:keyStyle="enterKeyStyle" - latin:keyWidth="fillRight" /> + <!-- Second row --> + <Row> + <switch> + <!-- Split keyboard layout for the second row --> + <case + latin:isSplitLayout="true" + > + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_left5" + latin:keyXPos="4.0%p" + latin:keyWidth="8.0%p" /> + <Spacer + latin:keyWidth="14.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_right4" + latin:keyWidth="8.0%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the second row --> + <default> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2" + latin:keyXPos="4.5%p" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="enterKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> </Row> - <Row - latin:keyWidth="9.0%p" - > - <Key - latin:keyStyle="shiftKeyStyle" - latin:keyWidth="10.0%p" /> - <include - latin:keyboardLayout="@xml/rowkeys_qwerty3" /> - <include - latin:keyboardLayout="@xml/keys_exclamation_question" /> - <Key - latin:keyStyle="shiftKeyStyle" - latin:keyWidth="fillRight" /> + <!-- Third row --> + <Row> + <switch> + <!-- Split keyboard layout for the third row --> + <case + latin:isSplitLayout="true" + > + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_left4" + latin:keyWidth="8.0%p" /> + <Spacer + latin:keyWidth="8.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_right3" + latin:keyWidth="8.0%p" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="8.0%p" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + </case> + <!-- Regular layout for the third row --> + <default> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="10.0%p" /> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3" + latin:keyWidth="9.0%p" /> + <include + latin:keyboardLayout="@xml/keys_exclamation_question" + latin:keyWidth="9.0%p" /> + <Key + latin:keyStyle="shiftKeyStyle" + latin:keyWidth="fillRight" /> + </default> + </switch> </Row> + <!-- Fourth row --> <include latin:keyboardLayout="@xml/row_qwerty4" /> </merge> diff --git a/java/res/xml/keyboard_layout_set_qwerty.xml b/java/res/xml/keyboard_layout_set_qwerty.xml index 821517081..1aa6f010a 100644 --- a/java/res/xml/keyboard_layout_set_qwerty.xml +++ b/java/res/xml/keyboard_layout_set_qwerty.xml @@ -23,7 +23,8 @@ <Element latin:elementName="alphabet" latin:elementKeyboard="@xml/kbd_qwerty" - latin:enableProximityCharsCorrection="true" /> + latin:enableProximityCharsCorrection="true" + latin:supportsSplitLayout="false" /> <Element latin:elementName="symbols" latin:elementKeyboard="@xml/kbd_symbols" /> diff --git a/java/res/xml/rowkeys_qwerty1.xml b/java/res/xml/rowkeys_qwerty1.xml index 8f3b160fe..b8e4a4c78 100644 --- a/java/res/xml/rowkeys_qwerty1.xml +++ b/java/res/xml/rowkeys_qwerty1.xml @@ -21,53 +21,10 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:keySpec="!text/keyspec_q" - latin:keyHintLabel="1" - latin:additionalMoreKeys="1" - latin:moreKeys="!text/morekeys_q" /> - <Key - latin:keySpec="!text/keyspec_w" - latin:keyHintLabel="2" - latin:additionalMoreKeys="2" - latin:moreKeys="!text/morekeys_w" /> - <Key - latin:keySpec="e" - latin:keyHintLabel="3" - latin:additionalMoreKeys="3" - latin:moreKeys="!text/morekeys_e" /> - <Key - latin:keySpec="r" - latin:keyHintLabel="4" - latin:additionalMoreKeys="4" - latin:moreKeys="!text/morekeys_r" /> - <Key - latin:keySpec="t" - latin:keyHintLabel="5" - latin:additionalMoreKeys="5" - latin:moreKeys="!text/morekeys_t" /> - <Key - latin:keySpec="!text/keyspec_y" - latin:keyHintLabel="6" - latin:additionalMoreKeys="6" - latin:moreKeys="!text/morekeys_y" /> - <Key - latin:keySpec="u" - latin:keyHintLabel="7" - latin:additionalMoreKeys="7" - latin:moreKeys="!text/morekeys_u" /> - <Key - latin:keySpec="i" - latin:keyHintLabel="8" - latin:additionalMoreKeys="8" - latin:moreKeys="!text/morekeys_i" /> - <Key - latin:keySpec="o" - latin:keyHintLabel="9" - latin:additionalMoreKeys="9" - latin:moreKeys="!text/morekeys_o" /> - <Key - latin:keySpec="p" - latin:keyHintLabel="0" - latin:additionalMoreKeys="0" /> + <!-- q,w,e,r,t --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_left5" /> + <!-- y,u,i,o,p --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty1_right5" /> </merge> diff --git a/java/res/xml/rowkeys_qwerty1_left5.xml b/java/res/xml/rowkeys_qwerty1_left5.xml new file mode 100644 index 000000000..ff9f1b2b5 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty1_left5.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="!text/keyspec_q" + latin:keyHintLabel="1" + latin:additionalMoreKeys="1" + latin:moreKeys="!text/morekeys_q" /> + <Key + latin:keySpec="!text/keyspec_w" + latin:keyHintLabel="2" + latin:additionalMoreKeys="2" + latin:moreKeys="!text/morekeys_w" /> + <Key + latin:keySpec="e" + latin:keyHintLabel="3" + latin:additionalMoreKeys="3" + latin:moreKeys="!text/morekeys_e" /> + <Key + latin:keySpec="r" + latin:keyHintLabel="4" + latin:additionalMoreKeys="4" + latin:moreKeys="!text/morekeys_r" /> + <Key + latin:keySpec="t" + latin:keyHintLabel="5" + latin:additionalMoreKeys="5" + latin:moreKeys="!text/morekeys_t" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty1_right5.xml b/java/res/xml/rowkeys_qwerty1_right5.xml new file mode 100644 index 000000000..2b3cae2e8 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty1_right5.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="!text/keyspec_y" + latin:keyHintLabel="6" + latin:additionalMoreKeys="6" + latin:moreKeys="!text/morekeys_y" /> + <Key + latin:keySpec="u" + latin:keyHintLabel="7" + latin:additionalMoreKeys="7" + latin:moreKeys="!text/morekeys_u" /> + <Key + latin:keySpec="i" + latin:keyHintLabel="8" + latin:additionalMoreKeys="8" + latin:moreKeys="!text/morekeys_i" /> + <Key + latin:keySpec="o" + latin:keyHintLabel="9" + latin:additionalMoreKeys="9" + latin:moreKeys="!text/morekeys_o" /> + <Key + latin:keySpec="p" + latin:keyHintLabel="0" + latin:additionalMoreKeys="0" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty2.xml b/java/res/xml/rowkeys_qwerty2.xml index 4077beaf6..550db3b3f 100644 --- a/java/res/xml/rowkeys_qwerty2.xml +++ b/java/res/xml/rowkeys_qwerty2.xml @@ -21,30 +21,10 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:keySpec="a" - latin:moreKeys="!text/morekeys_a" /> - <Key - latin:keySpec="s" - latin:moreKeys="!text/morekeys_s" /> - <Key - latin:keySpec="d" - latin:moreKeys="!text/morekeys_d" /> - <Key - latin:keySpec="f" /> - <Key - latin:keySpec="g" - latin:moreKeys="!text/morekeys_g" /> - <Key - latin:keySpec="h" - latin:moreKeys="!text/morekeys_h" /> - <Key - latin:keySpec="j" - latin:moreKeys="!text/morekeys_j" /> - <Key - latin:keySpec="k" - latin:moreKeys="!text/morekeys_k" /> - <Key - latin:keySpec="l" - latin:moreKeys="!text/morekeys_l" /> + <!-- a,s,d,f,g --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_left5" /> + <!-- h,j,k,l --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty2_right4" /> </merge> diff --git a/java/res/xml/rowkeys_qwerty2_left5.xml b/java/res/xml/rowkeys_qwerty2_left5.xml new file mode 100644 index 000000000..1803bf203 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty2_left5.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="a" + latin:moreKeys="!text/morekeys_a" /> + <Key + latin:keySpec="s" + latin:moreKeys="!text/morekeys_s" /> + <Key + latin:keySpec="d" + latin:moreKeys="!text/morekeys_d" /> + <Key + latin:keySpec="f" /> + <Key + latin:keySpec="g" + latin:moreKeys="!text/morekeys_g" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty2_right4.xml b/java/res/xml/rowkeys_qwerty2_right4.xml new file mode 100644 index 000000000..99936b7a2 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty2_right4.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="h" + latin:moreKeys="!text/morekeys_h" /> + <Key + latin:keySpec="j" + latin:moreKeys="!text/morekeys_j" /> + <Key + latin:keySpec="k" + latin:moreKeys="!text/morekeys_k" /> + <Key + latin:keySpec="l" + latin:moreKeys="!text/morekeys_l" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty3.xml b/java/res/xml/rowkeys_qwerty3.xml index 8562003d2..7a523f1f6 100644 --- a/java/res/xml/rowkeys_qwerty3.xml +++ b/java/res/xml/rowkeys_qwerty3.xml @@ -21,23 +21,10 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <Key - latin:keySpec="z" - latin:moreKeys="!text/morekeys_z" /> - <Key - latin:keySpec="!text/keyspec_x" - latin:moreKeys="!text/morekeys_x" /> - <Key - latin:keySpec="c" - latin:moreKeys="!text/morekeys_c" /> - <Key - latin:keySpec="v" - latin:moreKeys="!text/morekeys_v" /> - <Key - latin:keySpec="b" /> - <Key - latin:keySpec="n" - latin:moreKeys="!text/morekeys_n" /> - <Key - latin:keySpec="m" /> + <!-- z,x,c,v --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_left4" /> + <!-- b,n,m --> + <include + latin:keyboardLayout="@xml/rowkeys_qwerty3_right3" /> </merge> diff --git a/java/res/xml/rowkeys_qwerty3_left4.xml b/java/res/xml/rowkeys_qwerty3_left4.xml new file mode 100644 index 000000000..6043c3bba --- /dev/null +++ b/java/res/xml/rowkeys_qwerty3_left4.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="z" + latin:moreKeys="!text/morekeys_z" /> + <Key + latin:keySpec="!text/keyspec_x" + latin:moreKeys="!text/morekeys_x" /> + <Key + latin:keySpec="c" + latin:moreKeys="!text/morekeys_c" /> + <Key + latin:keySpec="v" + latin:moreKeys="!text/morekeys_v" /> +</merge> diff --git a/java/res/xml/rowkeys_qwerty3_right3.xml b/java/res/xml/rowkeys_qwerty3_right3.xml new file mode 100644 index 000000000..f69910344 --- /dev/null +++ b/java/res/xml/rowkeys_qwerty3_right3.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2014, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<merge + xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" +> + <Key + latin:keySpec="b" /> + <Key + latin:keySpec="n" + latin:moreKeys="!text/morekeys_n" /> + <Key + latin:keySpec="m" /> +</merge> diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index bd1c1479a..99a34bec7 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -702,6 +702,10 @@ public class Key implements Comparable<Key> { return ((mLabelFlags | defaultFlags) & LABEL_FLAGS_KEEP_BACKGROUND_ASPECT_RATIO) != 0; } + public final boolean hasCustomActionLabel() { + return (mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0; + } + private final boolean isShiftedLetterActivated() { return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0 && !TextUtils.isEmpty(mHintLabel); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 538e515bc..43c61443e 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -73,10 +73,12 @@ public final class KeyboardId { public final boolean mLanguageSwitchKeyEnabled; public final String mCustomActionLabel; public final boolean mHasShortcutKey; + public final boolean mIsSplitLayout; private final int mHashCode; - public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params) { + public KeyboardId(final int elementId, final KeyboardLayoutSet.Params params, + boolean isSplitLayout) { mSubtype = params.mSubtype; mLocale = SubtypeLocaleUtils.getSubtypeLocale(mSubtype); mWidth = params.mKeyboardWidth; @@ -89,6 +91,7 @@ public final class KeyboardId { mCustomActionLabel = (mEditorInfo.actionLabel != null) ? mEditorInfo.actionLabel.toString() : null; mHasShortcutKey = params.mVoiceInputKeyEnabled; + mIsSplitLayout = isSplitLayout; mHashCode = computeHashCode(this); } @@ -108,7 +111,8 @@ public final class KeyboardId { id.mCustomActionLabel, id.navigateNext(), id.navigatePrevious(), - id.mSubtype + id.mSubtype, + id.mIsSplitLayout }); } @@ -128,7 +132,8 @@ public final class KeyboardId { && TextUtils.equals(other.mCustomActionLabel, mCustomActionLabel) && other.navigateNext() == navigateNext() && other.navigatePrevious() == navigatePrevious() - && other.mSubtype.equals(mSubtype); + && other.mSubtype.equals(mSubtype) + && other.mIsSplitLayout == mIsSplitLayout; } private static boolean isAlphabetKeyboard(final int elementId) { @@ -175,7 +180,7 @@ public final class KeyboardId { @Override public String toString() { - return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s]", + return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s]", elementIdToName(mElementId), mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), mWidth, mHeight, @@ -187,7 +192,8 @@ public final class KeyboardId { (passwordInput() ? " passwordInput" : ""), (mHasShortcutKey ? " hasShortcutKey" : ""), (mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""), - (isMultiLine() ? " isMultiLine" : "") + (isMultiLine() ? " isMultiLine" : ""), + (mIsSplitLayout ? " isSplitLayout" : "") ); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index 3f4367313..1dbecdc81 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -96,6 +96,7 @@ public final class KeyboardLayoutSet { private static final class ElementParams { int mKeyboardXmlId; boolean mProximityCharsCorrectionEnabled; + boolean mSupportsSplitLayout; public ElementParams() {} } @@ -168,7 +169,10 @@ public final class KeyboardLayoutSet { // attribute in a keyboard_layout_set XML file. Also each keyboard layout XML resource is // specified as an elementKeyboard attribute in the file. // The KeyboardId is an internal key for a Keyboard object. - final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams); + + // TODO: AND mSupportsSplitLayout with the user preference that forces a split. + final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams, + elementParams.mSupportsSplitLayout); try { return getKeyboard(elementParams, id); } catch (final RuntimeException e) { @@ -376,6 +380,8 @@ public final class KeyboardLayoutSet { elementParams.mProximityCharsCorrectionEnabled = a.getBoolean( R.styleable.KeyboardLayoutSet_Element_enableProximityCharsCorrection, false); + elementParams.mSupportsSplitLayout = a.getBoolean( + R.styleable.KeyboardLayoutSet_Element_supportsSplitLayout, false); mParams.mKeyboardLayoutSetElementIdToParamsMap.put(elementName, elementParams); } finally { a.recycle(); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index d36c199e2..6cd7955ce 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -17,9 +17,7 @@ package com.android.inputmethod.keyboard; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Resources; -import android.preference.PreferenceManager; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.LayoutInflater; @@ -47,7 +45,6 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { private static final String TAG = KeyboardSwitcher.class.getSimpleName(); private SubtypeSwitcher mSubtypeSwitcher; - private SharedPreferences mPrefs; private InputView mCurrentInputView; private View mMainKeyboardFrame; @@ -76,13 +73,11 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } public static void init(final LatinIME latinIme) { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme); - sInstance.initInternal(latinIme, prefs); + sInstance.initInternal(latinIme); } - private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) { + private void initInternal(final LatinIME latinIme) { mLatinIME = latinIme; - mPrefs = prefs; mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mState = new KeyboardState(this); mIsHardwareAcceleratedDrawingEnabled = @@ -91,7 +86,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { public void updateKeyboardTheme() { final boolean themeUpdated = updateKeyboardThemeAndContextThemeWrapper( - mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs)); + mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */)); if (themeUpdated && mKeyboardView != null) { mLatinIME.setInputView(onCreateInputView(mIsHardwareAcceleratedDrawingEnabled)); } @@ -348,7 +343,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } updateKeyboardThemeAndContextThemeWrapper( - mLatinIME, KeyboardTheme.getKeyboardTheme(mPrefs)); + mLatinIME, KeyboardTheme.getKeyboardTheme(mLatinIME /* context */)); mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate( R.layout.input_view, null); mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java index 7161d3f26..6d8c8b76f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java @@ -16,14 +16,17 @@ package com.android.inputmethod.keyboard; +import android.content.Context; import android.content.SharedPreferences; import android.os.Build.VERSION_CODES; +import android.preference.PreferenceManager; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.R; +import java.util.ArrayList; import java.util.Arrays; public final class KeyboardTheme implements Comparable<KeyboardTheme> { @@ -40,7 +43,10 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { public static final int THEME_ID_LXX_DARK = 4; public static final int DEFAULT_THEME_ID = THEME_ID_KLP; - private static final KeyboardTheme[] KEYBOARD_THEMES = { + private static KeyboardTheme[] AVAILABLE_KEYBOARD_THEMES; + + @UsedForTesting + static final KeyboardTheme[] KEYBOARD_THEMES = { new KeyboardTheme(THEME_ID_ICS, "ICS", R.style.KeyboardTheme_ICS, // This has never been selected because we support ICS or later. VERSION_CODES.BASE), @@ -93,9 +99,10 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } @UsedForTesting - static KeyboardTheme searchKeyboardThemeById(final int themeId) { + static KeyboardTheme searchKeyboardThemeById(final int themeId, + final KeyboardTheme[] availableThemeIds) { // TODO: This search algorithm isn't optimal if there are many themes. - for (final KeyboardTheme theme : KEYBOARD_THEMES) { + for (final KeyboardTheme theme : availableThemeIds) { if (theme.mThemeId == themeId) { return theme; } @@ -105,13 +112,14 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { @UsedForTesting static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs, - final int sdkVersion) { + final int sdkVersion, final KeyboardTheme[] availableThemeArray) { final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null); if (klpThemeIdString != null) { if (sdkVersion <= VERSION_CODES.KITKAT) { try { final int themeId = Integer.parseInt(klpThemeIdString); - final KeyboardTheme theme = searchKeyboardThemeById(themeId); + final KeyboardTheme theme = searchKeyboardThemeById(themeId, + availableThemeArray); if (theme != null) { return theme; } @@ -125,22 +133,21 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { prefs.edit().remove(KLP_KEYBOARD_THEME_KEY).apply(); } // TODO: This search algorithm isn't optimal if there are many themes. - for (final KeyboardTheme theme : KEYBOARD_THEMES) { + for (final KeyboardTheme theme : availableThemeArray) { if (sdkVersion >= theme.mMinApiVersion) { return theme; } } - return searchKeyboardThemeById(DEFAULT_THEME_ID); + return searchKeyboardThemeById(DEFAULT_THEME_ID, availableThemeArray); } public static String getKeyboardThemeName(final int themeId) { - final KeyboardTheme theme = searchKeyboardThemeById(themeId); + final KeyboardTheme theme = searchKeyboardThemeById(themeId, KEYBOARD_THEMES); return theme.mThemeName; } - public static void saveKeyboardThemeId(final String themeIdString, - final SharedPreferences prefs) { - saveKeyboardThemeId(themeIdString, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); + public static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs) { + saveKeyboardThemeId(themeId, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); } @UsedForTesting @@ -152,25 +159,45 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } @UsedForTesting - static void saveKeyboardThemeId(final String themeIdString, - final SharedPreferences prefs, final int sdkVersion) { + static void saveKeyboardThemeId(final int themeId, final SharedPreferences prefs, + final int sdkVersion) { final String prefKey = getPreferenceKey(sdkVersion); - prefs.edit().putString(prefKey, themeIdString).apply(); + prefs.edit().putString(prefKey, Integer.toString(themeId)).apply(); } - public static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs) { - return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); + public static KeyboardTheme getKeyboardTheme(final Context context) { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + final KeyboardTheme[] availableThemeArray = getAvailableThemeArray(context); + return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT, availableThemeArray); + } + + static KeyboardTheme[] getAvailableThemeArray(final Context context) { + if (AVAILABLE_KEYBOARD_THEMES == null) { + final int[] availableThemeIdStringArray = context.getResources().getIntArray( + R.array.keyboard_theme_ids); + final ArrayList<KeyboardTheme> availableThemeList = new ArrayList<>(); + for (final int id : availableThemeIdStringArray) { + final KeyboardTheme theme = searchKeyboardThemeById(id, KEYBOARD_THEMES); + if (theme != null) { + availableThemeList.add(theme); + } + } + AVAILABLE_KEYBOARD_THEMES = availableThemeList.toArray( + new KeyboardTheme[availableThemeList.size()]); + } + return AVAILABLE_KEYBOARD_THEMES; } @UsedForTesting - static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion) { + static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion, + final KeyboardTheme[] availableThemeArray) { final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null); if (lxxThemeIdString == null) { - return getDefaultKeyboardTheme(prefs, sdkVersion); + return getDefaultKeyboardTheme(prefs, sdkVersion, availableThemeArray); } try { final int themeId = Integer.parseInt(lxxThemeIdString); - final KeyboardTheme theme = searchKeyboardThemeById(themeId); + final KeyboardTheme theme = searchKeyboardThemeById(themeId, availableThemeArray); if (theme != null) { return theme; } @@ -180,6 +207,6 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } // Remove preference that contains unknown or illegal theme id. prefs.edit().remove(LXX_KEYBOARD_THEME_KEY).apply(); - return getDefaultKeyboardTheme(prefs, sdkVersion); + return getDefaultKeyboardTheme(prefs, sdkVersion, availableThemeArray); } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index bb3cbb0eb..98cd1da54 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -343,7 +343,9 @@ public class KeyboardView extends View { final int keyWidth = key.getDrawWidth(); final int keyHeight = key.getHeight(); final int bgWidth, bgHeight, bgX, bgY; - if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags)) { + if (key.needsToKeepBackgroundAspectRatio(mDefaultKeyLabelFlags) + // HACK: To disable expanding normal/functional key background. + && !key.hasCustomActionLabel()) { final int intrinsicWidth = background.getIntrinsicWidth(); final int intrinsicHeight = background.getIntrinsicHeight(); final float minScale = Math.min( diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index c20546607..eced45ea5 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -61,9 +61,9 @@ public class DictionaryFacilitator { // dictionary. private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140; - private DictionaryGroup mDictionaryGroup = new DictionaryGroup(); + private DictionaryGroup[] mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup() }; private boolean mIsUserDictEnabled = false; - private volatile CountDownLatch mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0); + private volatile CountDownLatch mLatchForWaitingLoadingMainDictionaries = new CountDownLatch(0); // To synchronize assigning mDictionaryGroup to ensure closing dictionaries. private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; @@ -193,8 +193,9 @@ public class DictionaryFacilitator { mPersonalizationHelper.setIsMonolingualUser(isMonolingualUser); } + // TODO: remove this, replace with version returning multiple locales public Locale getLocale() { - return mDictionaryGroup.mLocale; + return mDictionaryGroups[0].mLocale; } private static ExpandableBinaryDictionary getSubDict(final String dictType, @@ -226,6 +227,21 @@ public class DictionaryFacilitator { usePersonalizedDicts, forceReloadMainDictionary, listener, "" /* dictNamePrefix */); } + private DictionaryGroup findDictionaryGroupWithLocale(final DictionaryGroup[] dictionaryGroups, + final Locale locale) { + for (int i = 0; i < dictionaryGroups.length; ++i) { + if (locale.equals(dictionaryGroups[i].mLocale)) { + return dictionaryGroups[i]; + } + } + return null; + } + + private DictionaryGroup getDictionaryGroupForActiveLanguage() { + // TODO: implement this + return mDictionaryGroups[0]; + } + public void resetDictionariesWithDictNamePrefix(final Context context, final Locale newLocaleToUse, final boolean useContactsDict, final boolean usePersonalizedDicts, @@ -252,7 +268,7 @@ public class DictionaryFacilitator { final ArrayList<String> dictsForLocale = new ArrayList<>(); existingDictsToCleanup.put(newLocale, dictsForLocale); final DictionaryGroup currentDictionaryGroupForLocale = - newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; + findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); if (null == currentDictionaryGroupForLocale) { continue; } @@ -266,10 +282,11 @@ public class DictionaryFacilitator { } } - final HashMap<Locale, DictionaryGroup> newDictionaryGroups = new HashMap<>(); - for (final Locale newLocale : newLocales) { + final DictionaryGroup[] newDictionaryGroups = new DictionaryGroup[newLocales.length]; + for (int i = 0; i < newLocales.length; ++i) { + final Locale newLocale = newLocales[i]; final DictionaryGroup dictionaryGroupForLocale = - newLocale.equals(mDictionaryGroup.mLocale) ? mDictionaryGroup : null; + findDictionaryGroupWithLocale(mDictionaryGroups, newLocale); final ArrayList<String> dictsToCleanupForLocale = existingDictsToCleanup.get(newLocale); final boolean noExistingDictsForThisLocale = (null == dictionaryGroupForLocale); @@ -297,30 +314,29 @@ public class DictionaryFacilitator { } subDicts.put(subDictType, subDict); } - newDictionaryGroups.put(newLocale, new DictionaryGroup(newLocale, mainDict, subDicts)); + newDictionaryGroups[i] = new DictionaryGroup(newLocale, mainDict, subDicts); } // Replace Dictionaries. - // TODO: use multiple locales. - final DictionaryGroup newDictionaryGroup = newDictionaryGroups.get(newLocaleToUse); - final DictionaryGroup oldDictionaryGroup; + final DictionaryGroup[] oldDictionaryGroups; synchronized (mLock) { - oldDictionaryGroup = mDictionaryGroup; - mDictionaryGroup = newDictionaryGroup; + oldDictionaryGroups = mDictionaryGroups; + mDictionaryGroups = newDictionaryGroups; mIsUserDictEnabled = UserBinaryDictionary.isEnabled(context); - if (null == newDictionaryGroup.getDict(Dictionary.TYPE_MAIN)) { + if (hasAtLeastOneUninitializedMainDictionary()) { asyncReloadUninitializedMainDictionaries(context, newLocales, listener); } } if (listener != null) { - listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); + listener.onUpdateMainDictionaryAvailability(hasAtLeastOneInitializedMainDictionary()); } // Clean up old dictionaries. for (final Locale localeToCleanUp : existingDictsToCleanup.keySet()) { final ArrayList<String> dictTypesToCleanUp = existingDictsToCleanup.get(localeToCleanUp); - final DictionaryGroup dictionarySetToCleanup = oldDictionaryGroup; + final DictionaryGroup dictionarySetToCleanup = + findDictionaryGroupWithLocale(oldDictionaryGroups, localeToCleanUp); for (final String dictType : dictTypesToCleanUp) { dictionarySetToCleanup.closeDict(dictType); } @@ -330,12 +346,18 @@ public class DictionaryFacilitator { private void asyncReloadUninitializedMainDictionaries(final Context context, final Locale[] locales, final DictionaryInitializationListener listener) { final CountDownLatch latchForWaitingLoadingMainDictionary = new CountDownLatch(1); - mLatchForWaitingLoadingMainDictionary = latchForWaitingLoadingMainDictionary; + mLatchForWaitingLoadingMainDictionaries = latchForWaitingLoadingMainDictionary; ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() { @Override public void run() { for (final Locale locale : locales) { - final DictionaryGroup dictionaryGroup = mDictionaryGroup; + final DictionaryGroup dictionaryGroup = + findDictionaryGroupWithLocale(mDictionaryGroups, locale); + if (null == dictionaryGroup) { + // This should never happen, but better safe than crashy + Log.w(TAG, "Expected a dictionary group for " + locale + " but none found"); + continue; + } final Dictionary mainDict = DictionaryFactory.createMainDictionaryFromManager(context, locale); synchronized (mLock) { @@ -348,7 +370,8 @@ public class DictionaryFacilitator { } } if (listener != null) { - listener.onUpdateMainDictionaryAvailability(hasInitializedMainDictionary()); + listener.onUpdateMainDictionaryAvailability( + hasAtLeastOneInitializedMainDictionary()); } latchForWaitingLoadingMainDictionary.countDown(); } @@ -381,17 +404,20 @@ public class DictionaryFacilitator { subDicts.put(dictType, dict); } } - mDictionaryGroup = new DictionaryGroup(locale, mainDictionary, subDicts); + mDictionaryGroups = new DictionaryGroup[] { + new DictionaryGroup(locale, mainDictionary, subDicts) }; } public void closeDictionaries() { - final DictionaryGroup dictionaryGroup; + final DictionaryGroup[] dictionaryGroups; synchronized (mLock) { - dictionaryGroup = mDictionaryGroup; - mDictionaryGroup = new DictionaryGroup(); + dictionaryGroups = mDictionaryGroups; + mDictionaryGroups = new DictionaryGroup[] { new DictionaryGroup() }; } - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { - dictionaryGroup.closeDict(dictType); + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + dictionaryGroup.closeDict(dictType); + } } mDistracterFilter.close(); if (mPersonalizationHelper != null) { @@ -401,40 +427,71 @@ public class DictionaryFacilitator { @UsedForTesting public ExpandableBinaryDictionary getSubDictForTesting(final String dictName) { - return mDictionaryGroup.getSubDict(dictName); + return mDictionaryGroups[0].getSubDict(dictName); } - // The main dictionary could have been loaded asynchronously. Don't cache the return value - // of this method. - public boolean hasInitializedMainDictionary() { - final Dictionary mainDict = mDictionaryGroup.getDict(Dictionary.TYPE_MAIN); - return mainDict != null && mainDict.isInitialized(); + // The main dictionaries are loaded asynchronously. Don't cache the return value + // of these methods. + public boolean hasAtLeastOneInitializedMainDictionary() { + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final Dictionary mainDict = dictionaryGroup.getDict(Dictionary.TYPE_MAIN); + if (mainDict != null && mainDict.isInitialized()) { + return true; + } + } + return false; + } + + public boolean hasAtLeastOneUninitializedMainDictionary() { + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final Dictionary mainDict = dictionaryGroup.getDict(Dictionary.TYPE_MAIN); + if (mainDict == null || !mainDict.isInitialized()) { + return true; + } + } + return false; } public boolean hasPersonalizationDictionary() { - return mDictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + if (dictionaryGroup.hasDict(Dictionary.TYPE_PERSONALIZATION)) { + return true; + } + } + return false; } public void flushPersonalizationDictionary() { - final ExpandableBinaryDictionary personalizationDictUsedForSuggestion = - mDictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); + final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion = + new HashSet<>(); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final ExpandableBinaryDictionary personalizationDictUsedForSuggestion = + dictionaryGroup.getSubDict(Dictionary.TYPE_PERSONALIZATION); + personalizationDictsUsedForSuggestion.add(personalizationDictUsedForSuggestion); + } mPersonalizationHelper.flushPersonalizationDictionariesToUpdate( - personalizationDictUsedForSuggestion); + personalizationDictsUsedForSuggestion); mDistracterFilter.close(); } - public void waitForLoadingMainDictionary(final long timeout, final TimeUnit unit) + public void waitForLoadingMainDictionaries(final long timeout, final TimeUnit unit) throws InterruptedException { - mLatchForWaitingLoadingMainDictionary.await(timeout, unit); + mLatchForWaitingLoadingMainDictionaries.await(timeout, unit); } @UsedForTesting public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit) throws InterruptedException { - waitForLoadingMainDictionary(timeout, unit); - final Map<String, ExpandableBinaryDictionary> dictMap = mDictionaryGroup.mSubDictMap; - for (final ExpandableBinaryDictionary dict : dictMap.values()) { - dict.waitAllTasksForTests(); + waitForLoadingMainDictionaries(timeout, unit); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final ExpandableBinaryDictionary dict : dictionaryGroup.mSubDictMap.values()) { + dict.waitAllTasksForTests(); + } } } @@ -453,7 +510,7 @@ public class DictionaryFacilitator { public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized, final PrevWordsInfo prevWordsInfo, final int timeStampInSeconds, final boolean blockPotentiallyOffensive) { - final DictionaryGroup dictionaryGroup = mDictionaryGroup; + final DictionaryGroup dictionaryGroup = getDictionaryGroupForActiveLanguage(); final String[] words = suggestion.split(Constants.WORD_SEPARATOR); PrevWordsInfo prevWordsInfoForCurrentWord = prevWordsInfo; for (int i = 0; i < words.length; i++) { @@ -520,7 +577,8 @@ public class DictionaryFacilitator { } private void removeWord(final String dictName, final String word) { - final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); + final ExpandableBinaryDictionary dictionary = + getDictionaryGroupForActiveLanguage().getSubDict(dictName); if (dictionary != null) { dictionary.removeUnigramEntryDynamically(word); } @@ -536,20 +594,22 @@ public class DictionaryFacilitator { public SuggestionResults getSuggestionResults(final WordComposer composer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) { - final DictionaryGroup dictionaryGroup = mDictionaryGroup; + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; final SuggestionResults suggestionResults = new SuggestionResults(SuggestedWords.MAX_SUGGESTIONS); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { - final Dictionary dictionary = dictionaryGroup.getDict(dictType); - if (null == dictionary) continue; - final ArrayList<SuggestedWordInfo> dictionarySuggestions = - dictionary.getSuggestions(composer, prevWordsInfo, proximityInfo, - settingsValuesForSuggestion, sessionId, languageWeight); - if (null == dictionarySuggestions) continue; - suggestionResults.addAll(dictionarySuggestions); - if (null != suggestionResults.mRawSuggestions) { - suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaryGroup.getDict(dictType); + if (null == dictionary) continue; + final ArrayList<SuggestedWordInfo> dictionarySuggestions = + dictionary.getSuggestions(composer, prevWordsInfo, proximityInfo, + settingsValuesForSuggestion, sessionId, languageWeight); + if (null == dictionarySuggestions) continue; + suggestionResults.addAll(dictionarySuggestions); + if (null != suggestionResults.mRawSuggestions) { + suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); + } } } return suggestionResults; @@ -559,20 +619,22 @@ public class DictionaryFacilitator { if (TextUtils.isEmpty(word)) { return false; } - final DictionaryGroup dictionaryGroup = mDictionaryGroup; - if (dictionaryGroup.mLocale == null) { - return false; - } - final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { - final Dictionary dictionary = dictionaryGroup.getDict(dictType); - // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and - // would be immutable once it's finished initializing, but concretely a null test is - // probably good enough for the time being. - if (null == dictionary) continue; - if (dictionary.isValidWord(word) - || (ignoreCase && dictionary.isValidWord(lowerCasedWord))) { - return true; + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + if (dictionaryGroup.mLocale == null) { + continue; + } + final String lowerCasedWord = word.toLowerCase(dictionaryGroup.mLocale); + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaryGroup.getDict(dictType); + // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and + // would be immutable once it's finished initializing, but concretely a null test is + // probably good enough for the time being. + if (null == dictionary) continue; + if (dictionary.isValidWord(word) + || (ignoreCase && dictionary.isValidWord(lowerCasedWord))) { + return true; + } } } return false; @@ -584,18 +646,20 @@ public class DictionaryFacilitator { return Dictionary.NOT_A_PROBABILITY; } int maxFreq = Dictionary.NOT_A_PROBABILITY; - final DictionaryGroup dictionaryGroup = mDictionaryGroup; - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { - final Dictionary dictionary = dictionaryGroup.getDict(dictType); - if (dictionary == null) continue; - final int tempFreq; - if (isGettingMaxFrequencyOfExactMatches) { - tempFreq = dictionary.getMaxFrequencyOfExactMatches(word); - } else { - tempFreq = dictionary.getFrequency(word); - } - if (tempFreq >= maxFreq) { - maxFreq = tempFreq; + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { + final Dictionary dictionary = dictionaryGroup.getDict(dictType); + if (dictionary == null) continue; + final int tempFreq; + if (isGettingMaxFrequencyOfExactMatches) { + tempFreq = dictionary.getMaxFrequencyOfExactMatches(word); + } else { + tempFreq = dictionary.getFrequency(word); + } + if (tempFreq >= maxFreq) { + maxFreq = tempFreq; + } } } return maxFreq; @@ -610,9 +674,12 @@ public class DictionaryFacilitator { } private void clearSubDictionary(final String dictName) { - final ExpandableBinaryDictionary dictionary = mDictionaryGroup.getSubDict(dictName); - if (dictionary != null) { - dictionary.clear(); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictName); + if (dictionary != null) { + dictionary.clear(); + } } } @@ -641,8 +708,10 @@ public class DictionaryFacilitator { public void addPhraseToContextualDictionary(final String[] phrase, final int probability, final int bigramProbabilityForWords, final int bigramProbabilityForPhrases) { + // TODO: we're inserting the phrase into the dictionary for the active language. Rethink + // this a bit from a theoretical point of view. final ExpandableBinaryDictionary contextualDict = - mDictionaryGroup.getSubDict(Dictionary.TYPE_CONTEXTUAL); + getDictionaryGroupForActiveLanguage().getSubDict(Dictionary.TYPE_CONTEXTUAL); if (contextualDict == null) { return; } @@ -675,22 +744,27 @@ public class DictionaryFacilitator { } public void dumpDictionaryForDebug(final String dictName) { - final ExpandableBinaryDictionary dictToDump = mDictionaryGroup.getSubDict(dictName); - if (dictToDump == null) { - Log.e(TAG, "Cannot dump " + dictName + ". " - + "The dictionary is not being used for suggestion or cannot be dumped."); - return; + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + final ExpandableBinaryDictionary dictToDump = dictionaryGroup.getSubDict(dictName); + if (dictToDump == null) { + Log.e(TAG, "Cannot dump " + dictName + ". " + + "The dictionary is not being used for suggestion or cannot be dumped."); + return; + } + dictToDump.dumpAllWordsForDebug(); } - dictToDump.dumpAllWordsForDebug(); } public ArrayList<Pair<String, DictionaryStats>> getStatsOfEnabledSubDicts() { final ArrayList<Pair<String, DictionaryStats>> statsOfEnabledSubDicts = new ArrayList<>(); - final DictionaryGroup dictionaryGroup = mDictionaryGroup; - for (final String dictType : SUB_DICT_TYPES) { - final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType); - if (dictionary == null) continue; - statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats())); + final DictionaryGroup[] dictionaryGroups = mDictionaryGroups; + for (final DictionaryGroup dictionaryGroup : dictionaryGroups) { + for (final String dictType : SUB_DICT_TYPES) { + final ExpandableBinaryDictionary dictionary = dictionaryGroup.getSubDict(dictType); + if (dictionary == null) continue; + statsOfEnabledSubDicts.add(new Pair<>(dictType, dictionary.getDictionaryStats())); + } } return statsOfEnabledSubDicts; } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java index fa0265d86..ff4a6bde1 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorLruCache.java @@ -84,7 +84,7 @@ public class DictionaryFacilitatorLruCache { private void waitForLoadingMainDictionary(final DictionaryFacilitator dictionaryFacilitator) { for (int i = 0; i < MAX_RETRY_COUNT_FOR_WAITING_FOR_LOADING_DICT; i++) { try { - dictionaryFacilitator.waitForLoadingMainDictionary( + dictionaryFacilitator.waitForLoadingMainDictionaries( WAIT_FOR_LOADING_MAIN_DICT_IN_MILLISECONDS, TimeUnit.MILLISECONDS); return; } catch (final InterruptedException e) { diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index a1dd67f27..671ba6714 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -156,23 +156,25 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } private void asyncExecuteTaskWithWriteLock(final Runnable task) { - asyncExecuteTaskWithLock(mLock.writeLock(), task); + asyncExecuteTaskWithLock(mLock.writeLock(), mDictName /* executorName */, task); } - private void asyncExecuteTaskWithLock(final Lock lock, final Runnable task) { - asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, task); + private void asyncExecuteTaskWithLock(final Lock lock, final String executorName, + final Runnable task) { + asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, executorName, task); } private void asyncPreCheckAndExecuteTaskWithWriteLock( final Callable<Boolean> preCheckTask, final Runnable task) { - asyncPreCheckAndExecuteTaskWithLock(mLock.writeLock(), preCheckTask, task); + asyncPreCheckAndExecuteTaskWithLock(mLock.writeLock(), preCheckTask, + mDictName /* executorName */, task); } // Execute task with lock when the result of preCheckTask is true or preCheckTask is null. private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, - final Callable<Boolean> preCheckTask, final Runnable task) { - ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { + final Callable<Boolean> preCheckTask, final String executorName, final Runnable task) { + ExecutorUtils.getExecutor(executorName).execute(new Runnable() { @Override public void run() { if (preCheckTask != null) { @@ -676,10 +678,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void dumpAllWordsForDebug() { reloadDictionaryIfRequired(); - asyncExecuteTaskWithLock(mLock.readLock(), new Runnable() { + asyncExecuteTaskWithLock(mLock.readLock(), "dumpAllWordsForDebug", new Runnable() { @Override public void run() { - Log.d(TAG, "Dump dictionary: " + mDictName); + Log.d(TAG, "Dump dictionary: " + mDictName + " for " + mLocale); try { final DictionaryHeader header = mBinaryDictionary.getHeader(); Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion()); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 0a64c4c26..475782042 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1000,7 +1000,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.cancelUpdateSuggestionStrip(); mainKeyboardView.setMainDictionaryAvailability( - mDictionaryFacilitator.hasInitializedMainDictionary()); + mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary()); mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn, currentSettingsValues.mKeyPreviewPopupDismissDelay); mainKeyboardView.setSlidingKeyInputPreviewEnabled( diff --git a/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java index 43cebdfa4..396d062f8 100644 --- a/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/PersonalizationHelperForDictionaryFacilitator.java @@ -88,17 +88,17 @@ public class PersonalizationHelperForDictionaryFacilitator { /** * Flush personalization dictionaries to dictionary files. Close dictionaries after writing - * files except the dictionary that is used for generating suggestions. + * files except the dictionaries that is used for generating suggestions. * - * @param personalizationDictUsedForSuggestion the personalization dictionary used for + * @param personalizationDictsUsedForSuggestion the personalization dictionaries used for * generating suggestions that won't be closed. */ public void flushPersonalizationDictionariesToUpdate( - final ExpandableBinaryDictionary personalizationDictUsedForSuggestion) { + final HashSet<ExpandableBinaryDictionary> personalizationDictsUsedForSuggestion) { for (final ExpandableBinaryDictionary personalizationDict : mPersonalizationDictsToUpdate.values()) { personalizationDict.asyncFlushBinaryDictionary(); - if (personalizationDict != personalizationDictUsedForSuggestion) { + if (!personalizationDictsUsedForSuggestion.contains(personalizationDict)) { // Close if the dictionary is not being used for suggestion. personalizationDict.close(); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 9e4aa40a2..4ad5ba65e 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -157,7 +157,7 @@ public final class Suggest { if (!isCorrectionEnabled || !allowsToBeAutoCorrected || resultsArePredictions || suggestionResults.isEmpty() || wordComposer.hasDigits() || wordComposer.isMostlyCaps() || wordComposer.isResumed() - || !mDictionaryFacilitator.hasInitializedMainDictionary() + || !mDictionaryFacilitator.hasAtLeastOneInitializedMainDictionary() || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) { // If we don't have a main dictionary, we never want to auto-correct. The reason for // this is, the user may have a contact whose name happens to match a valid word in @@ -235,7 +235,7 @@ public final class Suggest { SuggestedWordInfo.removeDups(null /* typedWord */, suggestionsContainer); // For some reason some suggestions with MIN_VALUE are making their way here. - // TODO: Find a more robust way to detect distractors. + // TODO: Find a more robust way to detect distracters. for (int i = suggestionsContainer.size() - 1; i >= 0; --i) { if (suggestionsContainer.get(i).mScore < SUPPRESS_SUGGEST_THRESHOLD) { suggestionsContainer.remove(i); diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 32d1fe372..567aa07f1 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -49,6 +49,7 @@ public final class WordComposer { private final ArrayList<Event> mEvents; private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH); private String mAutoCorrection; + private String mAutoCorrectionDictionaryType; private boolean mIsResumed; private boolean mIsBatchMode; // A memory of the last rejected batch mode suggestion, if any. This goes like this: the user @@ -418,8 +419,9 @@ public final class WordComposer { /** * Sets the auto-correction for this word. */ - public void setAutoCorrection(final String correction) { + public void setAutoCorrection(final String correction, String dictType) { mAutoCorrection = correction; + mAutoCorrectionDictionaryType = dictType; } /** @@ -430,6 +432,13 @@ public final class WordComposer { } /** + * @return the auto-correction dictionary type or null if none. + */ + public String getAutoCorrectionDictionaryTypeOrNull() { + return mAutoCorrectionDictionaryType; + } + + /** * @return whether we started composing this word by resuming suggestion on an existing string */ public boolean isResumed() { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index c5e60d677..0942c078f 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -612,14 +612,21 @@ public final class InputLogic { final SettingsValues settingsValues, final LatinIME.UIHandler handler) { if (SuggestedWords.EMPTY != suggestedWords) { final String autoCorrection; + final String dictType; if (suggestedWords.mWillAutoCorrect) { - autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); + SuggestedWordInfo info = suggestedWords.getInfo( + SuggestedWords.INDEX_OF_AUTO_CORRECTION); + autoCorrection = info.mWord; + dictType = info.mSourceDict.mDictType; } else { // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) // because it may differ from mWordComposer.mTypedWord. autoCorrection = suggestedWords.mTypedWord; + dictType = Dictionary.TYPE_USER_TYPED; } - mWordComposer.setAutoCorrection(autoCorrection); + // TODO: Use the SuggestedWordInfo to set the auto correction when + // user typed word is available via SuggestedWordInfo. + mWordComposer.setAutoCorrection(autoCorrection, dictType); } mSuggestedWords = suggestedWords; final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect; @@ -2100,6 +2107,8 @@ public final class InputLogic { mConnection.commitCorrection(new CorrectionInfo( mConnection.getExpectedSelectionEnd() - autoCorrection.length(), typedWord, autoCorrection)); + StatsUtils.onAutoCorrection(typedWord, autoCorrection, mWordComposer.isBatchMode(), + mWordComposer.getAutoCorrectionDictionaryTypeOrNull()); } } } diff --git a/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java index 5a3fc3600..29289aed2 100644 --- a/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/ThemeSettingsFragment.java @@ -17,7 +17,6 @@ package com.android.inputmethod.latin.settings; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; import android.preference.Preference; @@ -32,12 +31,12 @@ import com.android.inputmethod.latin.settings.RadioButtonPreference.OnRadioButto */ public final class ThemeSettingsFragment extends SubScreenFragment implements OnRadioButtonClickedListener { - private String mSelectedThemeId; + private int mSelectedThemeId; static class KeyboardThemePreference extends RadioButtonPreference { - final String mThemeId; + final int mThemeId; - KeyboardThemePreference(final Context context, final String name, final String id) { + KeyboardThemePreference(final Context context, final String name, final int id) { super(context); setTitle(name); mThemeId = id; @@ -45,14 +44,13 @@ public final class ThemeSettingsFragment extends SubScreenFragment } static void updateKeyboardThemeSummary(final Preference pref) { - final Resources res = pref.getContext().getResources(); - final SharedPreferences prefs = pref.getSharedPreferences(); - final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); - final String keyboardThemeId = String.valueOf(keyboardTheme.mThemeId); + final Context context = pref.getContext(); + final Resources res = context.getResources(); + final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(context); final String[] keyboardThemeNames = res.getStringArray(R.array.keyboard_theme_names); - final String[] keyboardThemeIds = res.getStringArray(R.array.keyboard_theme_ids); + final int[] keyboardThemeIds = res.getIntArray(R.array.keyboard_theme_ids); for (int index = 0; index < keyboardThemeNames.length; index++) { - if (keyboardThemeId.equals(keyboardThemeIds[index])) { + if (keyboardTheme.mThemeId == keyboardThemeIds[index]) { pref.setSummary(keyboardThemeNames[index]); return; } @@ -64,18 +62,18 @@ public final class ThemeSettingsFragment extends SubScreenFragment super.onCreate(icicle); addPreferencesFromResource(R.xml.prefs_screen_theme); final PreferenceScreen screen = getPreferenceScreen(); + final Context context = getActivity(); final Resources res = getResources(); final String[] keyboardThemeNames = res.getStringArray(R.array.keyboard_theme_names); - final String[] keyboardThemeIds = res.getStringArray(R.array.keyboard_theme_ids); + final int[] keyboardThemeIds = res.getIntArray(R.array.keyboard_theme_ids); for (int index = 0; index < keyboardThemeNames.length; index++) { final KeyboardThemePreference pref = new KeyboardThemePreference( - getActivity(), keyboardThemeNames[index], keyboardThemeIds[index]); + context, keyboardThemeNames[index], keyboardThemeIds[index]); screen.addPreference(pref); pref.setOnRadioButtonClickedListener(this); } - final SharedPreferences prefs = getSharedPreferences(); - final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(prefs); - mSelectedThemeId = String.valueOf(keyboardTheme.mThemeId); + final KeyboardTheme keyboardTheme = KeyboardTheme.getKeyboardTheme(context); + mSelectedThemeId = keyboardTheme.mThemeId; } @Override @@ -106,7 +104,7 @@ public final class ThemeSettingsFragment extends SubScreenFragment final Preference preference = screen.getPreference(index); if (preference instanceof KeyboardThemePreference) { final KeyboardThemePreference pref = (KeyboardThemePreference)preference; - final boolean selected = mSelectedThemeId.equals(pref.mThemeId); + final boolean selected = (mSelectedThemeId == pref.mThemeId); pref.setSelected(selected); } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 49b34d391..352391611 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -185,7 +185,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService try { final DictionaryFacilitator dictionaryFacilitator = mDictionaryFacilitatorCache.get(locale); - return dictionaryFacilitator.hasInitializedMainDictionary(); + return dictionaryFacilitator.hasAtLeastOneInitializedMainDictionary(); } finally { mSemaphore.release(); } diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java index 1db525502..f8a845304 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java @@ -64,9 +64,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr private final Object mLock = new Object(); // If the score of the top suggestion exceeds this value, the tested word (e.g., - // an OOV, a misspelling, or an in-vocabulary word) would be considered as a distractor to + // an OOV, a misspelling, or an in-vocabulary word) would be considered as a distracter to // words in dictionary. The greater the threshold is, the less likely the tested word would - // become a distractor, which means the tested word will be more likely to be added to + // become a distracter, which means the tested word will be more likely to be added to // the dictionary. private static final float DISTRACTER_WORD_SCORE_THRESHOLD = 0.4f; @@ -196,7 +196,7 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr } final boolean Word = dictionaryFacilitator.isValidWord(testedWord, false /* ignoreCase */); if (Word) { - // Valid word is not a distractor. + // Valid word is not a distracter. if (DEBUG) { Log.d(TAG, "isDistracter: false (valid word)"); } @@ -257,12 +257,12 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr return false; } final SuggestedWordInfo firstSuggestion = suggestionResults.first(); - final boolean isDistractor = suggestionExceedsDistracterThreshold( + final boolean isDistracter = suggestionExceedsDistracterThreshold( firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD); if (DEBUG) { - Log.d(TAG, "isDistracter: " + isDistractor); + Log.d(TAG, "isDistracter: " + isDistracter); } - return isDistractor; + return isDistracter; } private static boolean suggestionExceedsDistracterThreshold(final SuggestedWordInfo suggestion, |