diff options
| author | kr.angelov <kr.angelov@gmail.com> | 2013-11-13 10:45:20 +0000 |
|---|---|---|
| committer | kr.angelov <kr.angelov@gmail.com> | 2013-11-13 10:45:20 +0000 |
| commit | ab1856046ffca7187d7d806a454729d9fb1bbecc (patch) | |
| tree | 597f4487fd607d771a99f220509d647e7627564f /src/ui/android | |
| parent | fa4c327463f01eae2e0fda7deb82c4e9f40797cf (diff) | |
now the Android App has a translation keyboard which allows the translations to be done from inside another application
Diffstat (limited to 'src/ui/android')
31 files changed, 1181 insertions, 11 deletions
diff --git a/src/ui/android/AndroidManifest.xml b/src/ui/android/AndroidManifest.xml index d9f983e65..47a0073c7 100644 --- a/src/ui/android/AndroidManifest.xml +++ b/src/ui/android/AndroidManifest.xml @@ -25,6 +25,13 @@ </intent-filter> </activity> <activity android:name="LexicalEntryActivity"></activity> + <service android:name="TranslatorInputMethodService" + android:permission="android.permission.BIND_INPUT_METHOD"> + <intent-filter> + <action android:name="android.view.InputMethod" /> + </intent-filter> + <meta-data android:name="android.view.im" android:resource="@xml/method" /> + </service> </application> </manifest> diff --git a/src/ui/android/res/drawable-hdpi/btn_close.png b/src/ui/android/res/drawable-hdpi/btn_close.png Binary files differnew file mode 100644 index 000000000..47f11e5bf --- /dev/null +++ b/src/ui/android/res/drawable-hdpi/btn_close.png diff --git a/src/ui/android/res/drawable-hdpi/sym_keyboard_delete.png b/src/ui/android/res/drawable-hdpi/sym_keyboard_delete.png Binary files differnew file mode 100644 index 000000000..5139c7179 --- /dev/null +++ b/src/ui/android/res/drawable-hdpi/sym_keyboard_delete.png diff --git a/src/ui/android/res/drawable-hdpi/sym_keyboard_return.png b/src/ui/android/res/drawable-hdpi/sym_keyboard_return.png Binary files differnew file mode 100644 index 000000000..5a5670c32 --- /dev/null +++ b/src/ui/android/res/drawable-hdpi/sym_keyboard_return.png diff --git a/src/ui/android/res/drawable-hdpi/sym_keyboard_search.png b/src/ui/android/res/drawable-hdpi/sym_keyboard_search.png Binary files differnew file mode 100644 index 000000000..e72cde3bb --- /dev/null +++ b/src/ui/android/res/drawable-hdpi/sym_keyboard_search.png diff --git a/src/ui/android/res/drawable-hdpi/sym_keyboard_shift.png b/src/ui/android/res/drawable-hdpi/sym_keyboard_shift.png Binary files differnew file mode 100644 index 000000000..275769618 --- /dev/null +++ b/src/ui/android/res/drawable-hdpi/sym_keyboard_shift.png diff --git a/src/ui/android/res/drawable-hdpi/sym_keyboard_space.png b/src/ui/android/res/drawable-hdpi/sym_keyboard_space.png Binary files differnew file mode 100644 index 000000000..cef2daa5d --- /dev/null +++ b/src/ui/android/res/drawable-hdpi/sym_keyboard_space.png diff --git a/src/ui/android/res/drawable-mdpi/sym_keyboard_delete.png b/src/ui/android/res/drawable-mdpi/sym_keyboard_delete.png Binary files differnew file mode 100644 index 000000000..6cee59682 --- /dev/null +++ b/src/ui/android/res/drawable-mdpi/sym_keyboard_delete.png diff --git a/src/ui/android/res/drawable-mdpi/sym_keyboard_done.png b/src/ui/android/res/drawable-mdpi/sym_keyboard_done.png Binary files differnew file mode 100644 index 000000000..c0d6d1394 --- /dev/null +++ b/src/ui/android/res/drawable-mdpi/sym_keyboard_done.png diff --git a/src/ui/android/res/drawable-mdpi/sym_keyboard_return.png b/src/ui/android/res/drawable-mdpi/sym_keyboard_return.png Binary files differnew file mode 100644 index 000000000..cbe2b152f --- /dev/null +++ b/src/ui/android/res/drawable-mdpi/sym_keyboard_return.png diff --git a/src/ui/android/res/drawable-mdpi/sym_keyboard_search.png b/src/ui/android/res/drawable-mdpi/sym_keyboard_search.png Binary files differnew file mode 100644 index 000000000..127755d6b --- /dev/null +++ b/src/ui/android/res/drawable-mdpi/sym_keyboard_search.png diff --git a/src/ui/android/res/drawable-mdpi/sym_keyboard_shift.png b/src/ui/android/res/drawable-mdpi/sym_keyboard_shift.png Binary files differnew file mode 100644 index 000000000..d05962846 --- /dev/null +++ b/src/ui/android/res/drawable-mdpi/sym_keyboard_shift.png diff --git a/src/ui/android/res/drawable-mdpi/sym_keyboard_space.png b/src/ui/android/res/drawable-mdpi/sym_keyboard_space.png Binary files differnew file mode 100644 index 000000000..09b94d9e6 --- /dev/null +++ b/src/ui/android/res/drawable-mdpi/sym_keyboard_space.png diff --git a/src/ui/android/res/layout/input.xml b/src/ui/android/res/layout/input.xml new file mode 100644 index 000000000..fdef07a53 --- /dev/null +++ b/src/ui/android/res/layout/input.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> + +<org.grammaticalframework.ui.android.TranslatorKeyboardView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/keyboard" + android:layout_alignParentBottom="true" + android:layout_width="match_parent" + android:layout_height="wrap_content" + /> diff --git a/src/ui/android/res/layout/keyboard_languages_options.xml b/src/ui/android/res/layout/keyboard_languages_options.xml new file mode 100644 index 000000000..0b45b739c --- /dev/null +++ b/src/ui/android/res/layout/keyboard_languages_options.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="5dp" + android:layout_margin="1dp" + > + <ImageButton android:id="@+id/closeButton" + android:background="@android:color/transparent" + android:src="@drawable/btn_close" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_marginStart="8dp" + android:padding="5dp" + android:clickable="true" + /> +</LinearLayout>
\ No newline at end of file diff --git a/src/ui/android/res/values/dimens.xml b/src/ui/android/res/values/dimens.xml index 55c1e5908..55088756c 100644 --- a/src/ui/android/res/values/dimens.xml +++ b/src/ui/android/res/values/dimens.xml @@ -3,5 +3,8 @@ <!-- Default screen margins, per the Android Design guidelines. --> <dimen name="activity_horizontal_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen> + <dimen name="key_height">50dip</dimen> + <dimen name="candidate_font_height">16sp</dimen> + <dimen name="candidate_vertical_padding">6sp</dimen> </resources> diff --git a/src/ui/android/res/values/strings.xml b/src/ui/android/res/values/strings.xml index 57e20027d..e7c4e560f 100644 --- a/src/ui/android/res/values/strings.xml +++ b/src/ui/android/res/values/strings.xml @@ -9,4 +9,15 @@ <string name="mic_input">Speech Input</string> <string name="keyboard_input">Keyboard Input</string> <string name="global_preferences_key">org.grammaticalframework.ui.android.GLOBAL_PREFERENCES</string> + + <!-- Labels on soft keys --> + <string name="label_done_key">Done</string> + <string name="label_go_key">Go</string> + <string name="label_next_key">Next</string> + <string name="label_previous_key">Previous</string> + <string name="label_send_key">Send</string> + + <!-- Labels for subtype --> + <string name="normalKeyboardMode">normalKeyboardMode</string> + <string name="internalKeyboardMode">internalKeyboardMode</string> </resources> diff --git a/src/ui/android/res/xml/cyrillic.xml b/src/ui/android/res/xml/cyrillic.xml new file mode 100644 index 000000000..2e444507b --- /dev/null +++ b/src/ui/android/res/xml/cyrillic.xml @@ -0,0 +1,76 @@ +<?xml version="1.0" encoding="utf-8"?> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="9%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/key_height" + > + + <Row> + <Key android:codes="1103" android:keyLabel="я" android:keyEdgeFlags="left"/> + <Key android:codes="1074" android:keyLabel="в"/> + <Key android:codes="1077" android:keyLabel="е" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ёєэ"/> + <Key android:codes="1088" android:keyLabel="р"/> + <Key android:codes="1090" android:keyLabel="т" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ћ"/> + <Key android:codes="1098" android:keyLabel="ъ"/> + <Key android:codes="1091" android:keyLabel="у" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ў"/> + <Key android:codes="1080" android:keyLabel="и" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ії"/> + <Key android:codes="1086" android:keyLabel="о"/> + <Key android:codes="1087" android:keyLabel="п"/> + <Key android:codes="1095" android:keyLabel="ч" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ҷҹ" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="1072" android:keyLabel="а" android:keyEdgeFlags="left"/> + <Key android:codes="1089" android:keyLabel="с"/> + <Key android:codes="1076" android:keyLabel="д" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ђ"/> + <Key android:codes="1092" android:keyLabel="ф"/> + <Key android:codes="1075" android:keyLabel="г" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ґѓ"/> + <Key android:codes="1093" android:keyLabel="х"/> + <Key android:codes="1081" android:keyLabel="й" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ј"/> + <Key android:codes="1082" android:keyLabel="к" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ќ"/> + <Key android:codes="1083" android:keyLabel="л" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="љ"/> + <Key android:codes="1096" android:keyLabel="ш"/> + <Key android:codes="1097" android:keyLabel="щ" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="14%p" android:isModifier="true" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="1079" android:keyLabel="з"/> + <Key android:codes="1100" android:keyLabel="ь" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ыѣ"/> + <Key android:codes="1094" android:keyLabel="ц"/> + <Key android:codes="1078" android:keyLabel="ж" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="җџ"/> + <Key android:codes="1073" android:keyLabel="б"/> + <Key android:codes="1085" android:keyLabel="н" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="њ"/> + <Key android:codes="1084" android:keyLabel="м"/> + <Key android:codes="1102" android:keyLabel="ю"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:isRepeatable="true"/> + </Row> + + <Row android:rowEdgeFlags="bottom" android:keyboardMode="@string/normalKeyboardMode"> + <Key android:codes="-100" android:keyLabel="Sr" android:keyWidth="13%p" android:keyEdgeFlags="left"/> + <Key android:codes="-200" android:keyLabel="Tr" android:keyWidth="13%p"/> + <Key android:codes="-2" android:keyLabel="123" android:keyWidth="13%p"/> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:keyWidth="29%p" android:isRepeatable="true"/> + <Key android:codes="46,44" android:keyLabel=". ," + android:keyWidth="14%p"/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:keyWidth="19%p" android:keyEdgeFlags="right"/> + </Row> + + <Row android:rowEdgeFlags="bottom" android:keyboardMode="@string/internalKeyboardMode"> + <Key android:codes="-2" android:keyLabel="123" android:keyWidth="15%p" android:horizontalGap="10%p" android:keyEdgeFlags="left"/> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:keyWidth="30%p" android:isRepeatable="true"/> + <Key android:codes="46,44" android:keyLabel=". ," + android:keyWidth="15%p"/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:keyWidth="20%p" android:keyEdgeFlags="right"/> + </Row> +</Keyboard>
\ No newline at end of file diff --git a/src/ui/android/res/xml/method.xml b/src/ui/android/res/xml/method.xml new file mode 100644 index 000000000..af83761ca --- /dev/null +++ b/src/ui/android/res/xml/method.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- The attributes in this XML file provide configuration information --> +<!-- for the Search Manager. --> + +<input-method xmlns:android="http://schemas.android.com/apk/res/android"> +</input-method> diff --git a/src/ui/android/res/xml/popup_keyboard.xml b/src/ui/android/res/xml/popup_keyboard.xml new file mode 100644 index 000000000..521d3d278 --- /dev/null +++ b/src/ui/android/res/xml/popup_keyboard.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:keyHeight="10%p"> +</Keyboard>
\ No newline at end of file diff --git a/src/ui/android/res/xml/qwerty.xml b/src/ui/android/res/xml/qwerty.xml new file mode 100644 index 000000000..6e0b95975 --- /dev/null +++ b/src/ui/android/res/xml/qwerty.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/key_height" + > + + <Row> + <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/> + <Key android:codes="119" android:keyLabel="w"/> + <Key android:codes="101" android:keyLabel="e" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="èéêë"/> + <Key android:codes="114" android:keyLabel="r"/> + <Key android:codes="116" android:keyLabel="t"/> + <Key android:codes="121" android:keyLabel="y" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ýÿ"/> + <Key android:codes="117" android:keyLabel="u" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ùúûü"/> + <Key android:codes="105" android:keyLabel="i" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ìíîï"/> + <Key android:codes="111" android:keyLabel="o" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="òóôõöœø"/> + <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p" + android:keyEdgeFlags="left" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="àáâãäåæ"/> + <Key android:codes="115" android:keyLabel="s" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ß"/> + <Key android:codes="100" android:keyLabel="d"/> + <Key android:codes="102" android:keyLabel="f"/> + <Key android:codes="103" android:keyLabel="g"/> + <Key android:codes="104" android:keyLabel="h"/> + <Key android:codes="106" android:keyLabel="j"/> + <Key android:codes="107" android:keyLabel="k"/> + <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="122" android:keyLabel="z"/> + <Key android:codes="120" android:keyLabel="x"/> + <Key android:codes="99" android:keyLabel="c"/> + <Key android:codes="118" android:keyLabel="v"/> + <Key android:codes="98" android:keyLabel="b"/> + <Key android:codes="110" android:keyLabel="n" android:popupKeyboard="@xml/popup_keyboard" android:popupCharacters="ñ"/> + <Key android:codes="109" android:keyLabel="m"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:isRepeatable="true"/> + </Row> + + <Row android:rowEdgeFlags="bottom" android:keyboardMode="@string/normalKeyboardMode"> + <Key android:codes="-100" android:keyLabel="Sr" android:keyWidth="13%p" android:keyEdgeFlags="left"/> + <Key android:codes="-200" android:keyLabel="Tr" android:keyWidth="13%p"/> + <Key android:codes="-2" android:keyLabel="123" android:keyWidth="13%p"/> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:keyWidth="29%p" android:isRepeatable="true"/> + <Key android:codes="46,44" android:keyLabel=". ," + android:keyWidth="14%p"/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:keyWidth="19%p" android:keyEdgeFlags="right"/> + </Row> + + <Row android:rowEdgeFlags="bottom" android:keyboardMode="@string/internalKeyboardMode"> + <Key android:codes="-2" android:keyLabel="123" android:keyWidth="15%p" android:horizontalGap="10%p" android:keyEdgeFlags="left"/> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" + android:keyWidth="30%p" android:isRepeatable="true"/> + <Key android:codes="46,44" android:keyLabel=". ," + android:keyWidth="15%p"/> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:keyWidth="20%p" android:keyEdgeFlags="right"/> + </Row> +</Keyboard> +
\ No newline at end of file diff --git a/src/ui/android/res/xml/symbols.xml b/src/ui/android/res/xml/symbols.xml new file mode 100644 index 000000000..72deff01c --- /dev/null +++ b/src/ui/android/res/xml/symbols.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/key_height" + > + + <Row> + <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"/> + <Key android:codes="50" android:keyLabel="2"/> + <Key android:codes="51" android:keyLabel="3"/> + <Key android:codes="52" android:keyLabel="4"/> + <Key android:codes="53" android:keyLabel="5"/> + <Key android:codes="54" android:keyLabel="6"/> + <Key android:codes="55" android:keyLabel="7"/> + <Key android:codes="56" android:keyLabel="8"/> + <Key android:codes="57" android:keyLabel="9"/> + <Key android:codes="48" android:keyLabel="0" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/> + <Key android:codes="35" android:keyLabel="\#"/> + <Key android:codes="36" android:keyLabel="$"/> + <Key android:codes="37" android:keyLabel="%"/> + <Key android:codes="38" android:keyLabel="&"/> + <Key android:codes="42" android:keyLabel="*"/> + <Key android:codes="45" android:keyLabel="-"/> + <Key android:codes="61" android:keyLabel="="/> + <Key android:codes="40" android:keyLabel="("/> + <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="33" android:keyLabel="!" /> + <Key android:codes="34" android:keyLabel="""/> + <Key android:codes="39" android:keyLabel="\'"/> + <Key android:codes="58" android:keyLabel=":"/> + <Key android:codes="59" android:keyLabel=";"/> + <Key android:codes="47" android:keyLabel="/" /> + <Key android:codes="63" android:keyLabel="\?"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:isRepeatable="true"/> + </Row> + + <Row android:rowEdgeFlags="bottom" android:keyboardMode="@string/normalKeyboardMode"> + <Key android:codes="-200" android:keyLabel="Tr" android:horizontalGap="5%p" + android:keyWidth="13%p" android:keyEdgeFlags="left" /> + <Key android:codes="-2" android:keyLabel="ABC" android:keyWidth="15%p" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" android:keyWidth="30%p" + android:isRepeatable="true"/> + <Key android:codes="44" android:keyLabel="," android:keyWidth="15%p" /> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:keyWidth="20%p" android:keyEdgeFlags="right" + /> + </Row> + + <Row android:rowEdgeFlags="bottom" android:keyboardMode="@string/internalKeyboardMode"> + <Key android:codes="-2" android:keyLabel="ABC" android:keyWidth="15%p" android:horizontalGap="10%p"/> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" android:keyWidth="30%p" + android:isRepeatable="true"/> + <Key android:codes="44" android:keyLabel="," android:keyWidth="15%p" /> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:keyWidth="20%p" android:keyEdgeFlags="right" + /> + </Row> +</Keyboard>
\ No newline at end of file diff --git a/src/ui/android/res/xml/symbols_shift.xml b/src/ui/android/res/xml/symbols_shift.xml new file mode 100644 index 000000000..b55e6f521 --- /dev/null +++ b/src/ui/android/res/xml/symbols_shift.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8"?> + +<Keyboard xmlns:android="http://schemas.android.com/apk/res/android" + android:keyWidth="10%p" + android:horizontalGap="0px" + android:verticalGap="0px" + android:keyHeight="@dimen/key_height" + > + + <Row> + <Key android:codes="126" android:keyLabel="~" android:keyEdgeFlags="left"/> + <Key android:codes="177" android:keyLabel="±"/> + <Key android:codes="215" android:keyLabel="×"/> + <Key android:codes="247" android:keyLabel="÷"/> + <Key android:codes="8226" android:keyLabel="•"/> + <Key android:codes="176" android:keyLabel="°"/> + <Key android:codes="96" android:keyLabel="`"/> + <Key android:codes="180" android:keyLabel="´"/> + <Key android:codes="123" android:keyLabel="{"/> + <Key android:codes="125" android:keyLabel="}" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="169" android:keyLabel="©" android:keyEdgeFlags="left"/> + <Key android:codes="163" android:keyLabel="£"/> + <Key android:codes="8364" android:keyLabel="€"/> + <Key android:codes="94" android:keyLabel="^"/> + <Key android:codes="174" android:keyLabel="®"/> + <Key android:codes="165" android:keyLabel="¥"/> + <Key android:codes="95" android:keyLabel="_"/> + <Key android:codes="43" android:keyLabel="+"/> + <Key android:codes="91" android:keyLabel="["/> + <Key android:codes="93" android:keyLabel="]" android:keyEdgeFlags="right"/> + </Row> + + <Row> + <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift" + android:keyWidth="15%p" android:isModifier="true" + android:isSticky="true" android:keyEdgeFlags="left"/> + <Key android:codes="161" android:keyLabel="¡" /> + <Key android:codes="60" android:keyLabel="<"/> + <Key android:codes="62" android:keyLabel=">"/> + <Key android:codes="162" android:keyLabel="¢"/> + <Key android:codes="124" android:keyLabel="|"/> + <Key android:codes="92" android:keyLabel="\\" /> + <Key android:codes="191" android:keyLabel="¿"/> + <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete" + android:keyWidth="15%p" android:keyEdgeFlags="right" + android:isRepeatable="true"/> + </Row> + + <Row android:rowEdgeFlags="bottom" android:keyboardMode="@string/normalKeyboardMode"> + <Key android:codes="-200" android:keyLabel="Tr" android:horizontalGap="5%p" + android:keyWidth="13%p" android:keyEdgeFlags="left" /> + <Key android:codes="-2" android:keyLabel="ABC" android:keyWidth="15%p" /> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" android:keyWidth="30%p" + android:isRepeatable="true"/> + <Key android:codes="8230" android:keyLabel="…" android:keyWidth="15%p" /> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:keyWidth="20%p" android:keyEdgeFlags="right" /> + </Row> + + <Row android:rowEdgeFlags="bottom" android:keyboardMode="@string/internalKeyboardMode"> + <Key android:codes="-2" android:keyLabel="ABC" android:keyWidth="15%p" android:horizontalGap="10%p"/> + <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" android:keyWidth="30%p" + android:isRepeatable="true"/> + <Key android:codes="8230" android:keyLabel="…" android:keyWidth="15%p" /> + <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return" + android:keyWidth="20%p" android:keyEdgeFlags="right" /> + </Row> +</Keyboard> diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/ConversationView.java b/src/ui/android/src/org/grammaticalframework/ui/android/ConversationView.java index cda719604..0a1004462 100644 --- a/src/ui/android/src/org/grammaticalframework/ui/android/ConversationView.java +++ b/src/ui/android/src/org/grammaticalframework/ui/android/ConversationView.java @@ -1,6 +1,7 @@ package org.grammaticalframework.ui.android; import android.content.Context; +import android.os.Bundle; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -88,6 +89,9 @@ public class ConversationView extends ScrollView { return false; } }); + Bundle extras = edittext.getInputExtras(true); + extras.putBoolean("show_language_toggle", false); + mContent.addView(view); post(new Runnable() { public void run() { diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/Language.java b/src/ui/android/src/org/grammaticalframework/ui/android/Language.java index 996846e82..6b63bb8f2 100644 --- a/src/ui/android/src/org/grammaticalframework/ui/android/Language.java +++ b/src/ui/android/src/org/grammaticalframework/ui/android/Language.java @@ -9,12 +9,15 @@ public class Language implements Serializable { private final String mLangName; private final String mConcrete; private final int mInflResource; + private final int mKeyboardResource; - public Language(String langCode, String langName, String concrete, int inflResource) { + public Language(String langCode, String langName, String concrete, + int inflResource, int keyboardResource) { mLangCode = langCode; mLangName = langName; mConcrete = concrete; mInflResource = inflResource; + mKeyboardResource = keyboardResource; } public String getLangCode() { @@ -28,6 +31,10 @@ public class Language implements Serializable { public int getInflectionResource() { return mInflResource; } + + public int getKeyboardResource() { + return mKeyboardResource; + } String getConcrete() { return mConcrete; diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/LexicalEntryActivity.java b/src/ui/android/src/org/grammaticalframework/ui/android/LexicalEntryActivity.java index 8c8777e2f..8d9a41955 100644 --- a/src/ui/android/src/org/grammaticalframework/ui/android/LexicalEntryActivity.java +++ b/src/ui/android/src/org/grammaticalframework/ui/android/LexicalEntryActivity.java @@ -45,7 +45,7 @@ public class LexicalEntryActivity extends ListActivity { mTranslator = ((GFTranslator) getApplicationContext()).getTranslator(); mShowLanguageView = (LanguageSelector) findViewById(R.id.show_language); - mShowLanguageView.setLanguages(mTranslator.getAvailableSourceLanguages()); + mShowLanguageView.setLanguages(mTranslator.getAvailableLanguages()); mShowLanguageView.setOnLanguageSelectedListener(new OnLanguageSelectedListener() { @Override public void onLanguageSelected(Language language) { diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java b/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java index ab14bbfe2..9cbeb6930 100644 --- a/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java +++ b/src/ui/android/src/org/grammaticalframework/ui/android/MainActivity.java @@ -101,14 +101,14 @@ public class MainActivity extends Activity { mTranslator = ((GFTranslator) getApplicationContext()).getTranslator(); - mSourceLanguageView.setLanguages(mTranslator.getAvailableSourceLanguages()); + mSourceLanguageView.setLanguages(mTranslator.getAvailableLanguages()); mSourceLanguageView.setOnLanguageSelectedListener(new OnLanguageSelectedListener() { @Override public void onLanguageSelected(Language language) { onSourceLanguageSelected(language); } }); - mTargetLanguageView.setLanguages(mTranslator.getAvailableTargetLanguages()); + mTargetLanguageView.setLanguages(mTranslator.getAvailableLanguages()); mTargetLanguageView.setOnLanguageSelectedListener(new OnLanguageSelectedListener() { @Override public void onLanguageSelected(Language language) { @@ -192,10 +192,16 @@ public class MainActivity extends Activity { void onSourceLanguageSelected(Language language) { mTranslator.setSourceLanguage(language); + if (TranslatorInputMethodService.getInstance() != null) { + TranslatorInputMethodService.getInstance().handleChangeSourceLanguage(language); + } } void onTargetLanguageSelected(Language language) { mTranslator.setTargetLanguage(language); + if (TranslatorInputMethodService.getInstance() != null) { + TranslatorInputMethodService.getInstance().handleChangeTargetLanguage(language); + } } public String getSourceLanguageCode() { @@ -211,6 +217,10 @@ public class MainActivity extends Activity { Language newTarget = mTranslator.getSourceLanguage(); mSourceLanguageView.setSelectedLanguage(newSource); mTargetLanguageView.setSelectedLanguage(newTarget); + + if (TranslatorInputMethodService.getInstance() != null) { + TranslatorInputMethodService.getInstance().handleSwitchLanguages(); + } } private void startRecognition() { diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/Translator.java b/src/ui/android/src/org/grammaticalframework/ui/android/Translator.java index f3a247844..e8ef4738e 100644 --- a/src/ui/android/src/org/grammaticalframework/ui/android/Translator.java +++ b/src/ui/android/src/org/grammaticalframework/ui/android/Translator.java @@ -33,8 +33,9 @@ public class Translator { new Language("fi-FI", "Finnish", "TranslateFin", 0), new Language("sv-SE", "Swedish", "TranslateSwe", R.xml.inflection_sv), */ - new Language("en-US", "English", "ParseEng", R.xml.inflection_en), - new Language("bg-BG", "Bulgarian", "ParseBul", R.xml.inflection_bg), + new Language("en-US", "English", "ParseEng", R.xml.inflection_en, R.xml.qwerty), + new Language("bg-BG", "Bulgarian", "ParseBul", R.xml.inflection_bg, R.xml.cyrillic), + new Language("sv-SE", "Swedish", "ParseSwe", R.xml.inflection_sv, R.xml.qwerty), }; private Language mSourceLanguage; @@ -78,11 +79,7 @@ public class Translator { mTargetLanguage = getPrefLang(TARGET_LANG_KEY, 1); } - public List<Language> getAvailableSourceLanguages() { - return Arrays.asList(mLanguages); - } - - public List<Language> getAvailableTargetLanguages() { + public List<Language> getAvailableLanguages() { return Arrays.asList(mLanguages); } diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorInputMethodService.java b/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorInputMethodService.java new file mode 100644 index 000000000..29c52c44a --- /dev/null +++ b/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorInputMethodService.java @@ -0,0 +1,581 @@ +package org.grammaticalframework.ui.android; + +import android.inputmethodservice.InputMethodService; +import android.text.InputType; +import android.text.method.MetaKeyKeyListener; +import android.util.Log; +import android.view.KeyCharacterMap; +import android.view.KeyEvent; +import android.view.View; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +import java.util.ArrayList; +import java.util.List; + + +public class TranslatorInputMethodService extends InputMethodService + implements android.inputmethodservice.KeyboardView.OnKeyboardActionListener { + + private TranslatorKeyboardView mInputView; + private CompletionsView mCandidateView; + private CompletionInfo[] mCompletions; + + private StringBuilder mComposing = new StringBuilder(); + private boolean mPredictionOn; + private boolean mCompletionOn; + private boolean mCapsLock; + private long mLastShiftTime; + private long mMetaState; + + private TranslatorKeyboard mSymbolsKeyboard; + private TranslatorKeyboard mSymbolsShiftedKeyboard; + private TranslatorKeyboard mLanguageKeyboard; + + private TranslatorKeyboard mCurKeyboard; + + private int mActionId; + + private Translator mTranslator; + + @Override + public void onCreate() { + super.onCreate(); + + mTranslator = ((GFTranslator) getApplicationContext()).getTranslator(); + + mSymbolsKeyboard = null; + mSymbolsShiftedKeyboard = null; + mLanguageKeyboard = null; + } + + @Override + public View onCreateInputView() { + mInputView = (TranslatorKeyboardView) + getLayoutInflater().inflate(R.layout.input, null); + mInputView.setOnKeyboardActionListener(this); + mInputView.setKeyboard(mCurKeyboard); + return mInputView; + } + + @Override + public View onCreateCandidatesView() { + mCandidateView = new CompletionsView(this); + mCandidateView.setService(this); + return mCandidateView; + } + + private int mModeId; + private static TranslatorInputMethodService mInstance; + + static TranslatorInputMethodService getInstance() { + return mInstance; + } + + @Override + public void onStartInput(EditorInfo attribute, boolean restarting) { + super.onStartInput(attribute, restarting); + + // Reset our state. We want to do this even if restarting, because + // the underlying state of the text editor could have changed in any way. + mComposing.setLength(0); + updateCandidates(); + + if (!restarting) { + // Clear shift states. + mMetaState = 0; + } + + mPredictionOn = false; + mCompletionOn = false; + mCompletions = null; + + int res = + mTranslator.getSourceLanguage().getKeyboardResource(); + mModeId = R.string.normalKeyboardMode; + if (attribute.extras != null && + !attribute.extras.getBoolean("show_language_toggle", true)) { + mModeId = R.string.internalKeyboardMode; + } + mLanguageKeyboard = new TranslatorKeyboard(this, res, mModeId); + mSymbolsKeyboard = new TranslatorKeyboard(this, R.xml.symbols, mModeId); + mSymbolsShiftedKeyboard = new TranslatorKeyboard(this, R.xml.symbols_shift, mModeId); + + // We are now going to initialize our state based on the type of + // text being edited. + switch (attribute.inputType & InputType.TYPE_MASK_CLASS) { + case InputType.TYPE_CLASS_NUMBER: + case InputType.TYPE_CLASS_DATETIME: + // Numbers and dates default to the symbols keyboard, with + // no extra features. + mCurKeyboard = mSymbolsKeyboard; + break; + + case InputType.TYPE_CLASS_PHONE: + // Phones will also default to the symbols keyboard, though + // often you will want to have a dedicated phone keyboard. + mCurKeyboard = mSymbolsKeyboard; + break; + + case InputType.TYPE_CLASS_TEXT: + // This is general text editing. We will default to the + // normal alphabetic keyboard, and assume that we should + // be doing predictive text (showing candidates as the + // user types). + mCurKeyboard = mLanguageKeyboard; + mPredictionOn = true; + + // We now look for a few special variations of text that will + // modify our behavior. + int variation = attribute.inputType & InputType.TYPE_MASK_VARIATION; + if (variation == InputType.TYPE_TEXT_VARIATION_PASSWORD || + variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) { + // Do not display predictions / what the user is typing + // when they are entering a password. + mPredictionOn = false; + } + + if (variation == InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS + || variation == InputType.TYPE_TEXT_VARIATION_URI + || variation == InputType.TYPE_TEXT_VARIATION_FILTER) { + // Our predictions are not useful for e-mail addresses + // or URIs. + mPredictionOn = false; + } + + if ((attribute.inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) { + // If this is an auto-complete text view, then our predictions + // will not be shown and instead we will allow the editor + // to supply their own. We only show the editor's + // candidates when in full-screen mode, otherwise relying + // own it displaying its own UI. + mPredictionOn = false; + mCompletionOn = isFullscreenMode(); + } + + // We also want to look at the current state of the editor + // to decide whether our alphabetic keyboard should start out + // shifted. + updateShiftKeyState(attribute); + break; + + default: + // For all unknown input types, default to the alphabetic + // keyboard with no special features. + mCurKeyboard = mLanguageKeyboard; + updateShiftKeyState(attribute); + } + + mActionId = attribute.imeOptions & EditorInfo.IME_MASK_ACTION; + mCurKeyboard.setImeOptions(getResources(), attribute.imeOptions); + + mInstance = this; + } + + @Override + public void onFinishInput() { + super.onFinishInput(); + + // Clear current composing text and candidates. + mComposing.setLength(0); + updateCandidates(); + + // We only hide the candidates window when finishing input on + // a particular editor, to avoid popping the underlying application + // up and down if the user is entering text into the bottom of + // its window. + setCandidatesViewShown(false); + + mCurKeyboard = mLanguageKeyboard; + if (mInputView != null) { + mInputView.closing(); + } + + mInstance = null; + } + + @Override + public void onStartInputView(EditorInfo attribute, boolean restarting) { + super.onStartInputView(attribute, restarting); + // Apply the selected keyboard to the input view. + mInputView.setKeyboard(mCurKeyboard); + mInputView.closing(); + } + + @Override + public void onUpdateSelection(int oldSelStart, int oldSelEnd, + int newSelStart, int newSelEnd, + int candidatesStart, int candidatesEnd) { + super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, + candidatesStart, candidatesEnd); + + // If the current selection in the text view changes, we should + // clear whatever candidate text we have. + if (mComposing.length() > 0 && (newSelStart != candidatesEnd + || newSelEnd != candidatesEnd)) { + mComposing.setLength(0); + updateCandidates(); + InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.finishComposingText(); + } + } + } + + @Override + public void onDisplayCompletions(CompletionInfo[] completions) { + if (mCompletionOn) { + mCompletions = completions; + if (completions == null) { + setSuggestions(null, false, false); + return; + } + + List<String> stringList = new ArrayList<String>(); + for (int i = 0; i < completions.length; i++) { + CompletionInfo ci = completions[i]; + if (ci != null) stringList.add(ci.getText().toString()); + } + setSuggestions(stringList, true, true); + } + } + + /** + * This translates incoming hard key events in to edit operations on an + * InputConnection. It is only needed when using the + * PROCESS_HARD_KEYS option. + */ + private boolean translateKeyDown(int keyCode, KeyEvent event) { + mMetaState = MetaKeyKeyListener.handleKeyDown(mMetaState, + keyCode, event); + int c = event.getUnicodeChar(MetaKeyKeyListener.getMetaState(mMetaState)); + mMetaState = MetaKeyKeyListener.adjustMetaAfterKeypress(mMetaState); + InputConnection ic = getCurrentInputConnection(); + if (c == 0 || ic == null) { + return false; + } + + if ((c & KeyCharacterMap.COMBINING_ACCENT) != 0) { + c = c & KeyCharacterMap.COMBINING_ACCENT_MASK; + } + + if (mComposing.length() > 0) { + char accent = mComposing.charAt(mComposing.length() -1 ); + int composed = KeyEvent.getDeadChar(accent, c); + + if (composed != 0) { + c = composed; + mComposing.setLength(mComposing.length()-1); + } + } + + onKey(c, null); + + return true; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_BACK: + // The InputMethodService already takes care of the back + // key for us, to dismiss the input method if it is shown. + // However, our keyboard could be showing a pop-up window + // that back should dismiss, so we first allow it to do that. + if (event.getRepeatCount() == 0 && mInputView != null) { + if (mInputView.handleBack()) { + return true; + } + } + break; + + case KeyEvent.KEYCODE_DEL: + // Special handling of the delete key: if we currently are + // composing text for the user, we want to modify that instead + // of let the application to the delete itself. + if (mComposing.length() > 0) { + onKey(TranslatorKeyboard.KEYCODE_DELETE, null); + return true; + } + break; + + case KeyEvent.KEYCODE_ENTER: + // Let the underlying text editor always handle these. + return false; + + default: + if (mPredictionOn && translateKeyDown(keyCode, event)) { + return true; + } + } + + return super.onKeyDown(keyCode, event); + } + + /** + * Helper function to commit any text being composed in to the editor. + */ + private void commitTyped(InputConnection inputConnection) { + if (mComposing.length() > 0) { + inputConnection.commitText(mComposing, mComposing.length()); + mComposing.setLength(0); + updateCandidates(); + } + } + + /** + * Helper to update the shift state of our keyboard based on the initial + * editor state. + */ + private void updateShiftKeyState(EditorInfo attr) { + if (attr != null + && mInputView != null && mLanguageKeyboard == mInputView.getKeyboard()) { + int caps = 0; + EditorInfo ei = getCurrentInputEditorInfo(); + if (ei != null && ei.inputType != InputType.TYPE_NULL) { + caps = getCurrentInputConnection().getCursorCapsMode(attr.inputType); + } + mInputView.setShifted(mCapsLock || caps != 0); + } + } + + /** + * Helper to send a key down / key up pair to the current editor. + */ + private void keyDownUp(int keyEventCode) { + getCurrentInputConnection().sendKeyEvent( + new KeyEvent(KeyEvent.ACTION_DOWN, keyEventCode)); + getCurrentInputConnection().sendKeyEvent( + new KeyEvent(KeyEvent.ACTION_UP, keyEventCode)); + } + + // Implementation of KeyboardViewListener + public void onKey(int primaryCode, int[] keyCodes) { + if (primaryCode == TranslatorKeyboard.KEYCODE_DELETE) { + handleBackspace(); + } else if (primaryCode == TranslatorKeyboard.KEYCODE_SHIFT) { + handleShift(); + } else if (primaryCode == TranslatorKeyboard.KEYCODE_SOURCE_LANGUAGE + && mInputView != null) { + Language newSource = mTranslator.getTargetLanguage(); + Language newTarget = mTranslator.getSourceLanguage(); + mTranslator.setSourceLanguage(newSource); + mTranslator.setTargetLanguage(newTarget); + handleSwitchLanguages(); + } else if (primaryCode < TranslatorKeyboard.KEYCODE_SOURCE_LANGUAGE && + primaryCode > TranslatorKeyboard.KEYCODE_SOURCE_LANGUAGE-TranslatorKeyboard.MAX_LANGUAGE_KEYCODES) { + Language newSource = + mTranslator.getAvailableLanguages().get(TranslatorKeyboard.KEYCODE_SOURCE_LANGUAGE-primaryCode-1); + mTranslator.setSourceLanguage(newSource); + handleChangeSourceLanguage(newSource); + } else if (primaryCode == TranslatorKeyboard.KEYCODE_TARGET_LANGUAGE) { + String translation = mTranslator.translate(mComposing.toString()); + getCurrentInputConnection().commitText(translation, 1); + return; + } else if (primaryCode < TranslatorKeyboard.KEYCODE_TARGET_LANGUAGE && + primaryCode > TranslatorKeyboard.KEYCODE_TARGET_LANGUAGE-TranslatorKeyboard.MAX_LANGUAGE_KEYCODES) { + Language newTarget = + mTranslator.getAvailableLanguages().get(TranslatorKeyboard.KEYCODE_TARGET_LANGUAGE-primaryCode-1); + mTranslator.setTargetLanguage(newTarget); + handleChangeTargetLanguage(newTarget); + } else if (primaryCode == TranslatorKeyboard.KEYCODE_MODE_CHANGE + && mInputView != null) { + TranslatorKeyboard current = (TranslatorKeyboard) mInputView.getKeyboard(); + if (current == mSymbolsKeyboard || current == mSymbolsShiftedKeyboard) { + current = mLanguageKeyboard; + } else { + current = mSymbolsKeyboard; + } + mInputView.setKeyboard(current); + if (current == mSymbolsKeyboard) { + current.setShifted(false); + } + } else if (primaryCode == 10) { + getCurrentInputConnection().performEditorAction(mActionId); + } else if (primaryCode == ' ' && mComposing.length() == 0) { + getCurrentInputConnection().commitText(" ", 1); + } else { + handleCharacter(primaryCode, keyCodes); + } + } + + public void onText(CharSequence text) { + InputConnection ic = getCurrentInputConnection(); + if (ic == null) return; + ic.beginBatchEdit(); + if (mComposing.length() > 0) { + commitTyped(ic); + } + ic.commitText(text, 0); + ic.endBatchEdit(); + updateShiftKeyState(getCurrentInputEditorInfo()); + } + + /** + * Update the list of available candidates from the current composing + * text. This will need to be filled in by however you are determining + * candidates. + */ + private void updateCandidates() { + if (!mCompletionOn) { + if (mComposing.length() > 0) { + ArrayList<String> list = new ArrayList<String>(); + list.add(mComposing.toString()); + list.add("alfa"); + list.add("beta"); + setSuggestions(list, true, true); + Log.d("KEYBOARD", mComposing.toString()); + } else { + setSuggestions(null, false, false); + } + } + } + + public void setSuggestions(List<String> suggestions, boolean completions, + boolean typedWordValid) { + if (suggestions != null && suggestions.size() > 0) { + setCandidatesViewShown(true); + } else if (isExtractViewShown()) { + setCandidatesViewShown(true); + } + if (mCandidateView != null) { + mCandidateView.setSuggestions(suggestions, completions, typedWordValid); + } + } + + private void handleBackspace() { + final int length = mComposing.length(); + if (length > 1) { + mComposing.delete(length - 1, length); + getCurrentInputConnection().setComposingText(mComposing, 1); + updateCandidates(); + } else if (length > 0) { + mComposing.setLength(0); + getCurrentInputConnection().commitText("", 0); + updateCandidates(); + } else { + keyDownUp(KeyEvent.KEYCODE_DEL); + } + updateShiftKeyState(getCurrentInputEditorInfo()); + } + + private void handleShift() { + if (mInputView == null) { + return; + } + + TranslatorKeyboard currentKeyboard = (TranslatorKeyboard) mInputView.getKeyboard(); + if (mLanguageKeyboard == currentKeyboard) { + // Alphabet keyboard + checkToggleCapsLock(); + mInputView.setShifted(mCapsLock || !mInputView.isShifted()); + } else if (currentKeyboard == mSymbolsKeyboard) { + mSymbolsKeyboard.setShifted(true); + mInputView.setKeyboard(mSymbolsShiftedKeyboard); + mSymbolsShiftedKeyboard.setShifted(true); + } else if (currentKeyboard == mSymbolsShiftedKeyboard) { + mSymbolsShiftedKeyboard.setShifted(false); + mInputView.setKeyboard(mSymbolsKeyboard); + mSymbolsKeyboard.setShifted(false); + } + } + + private void handleCharacter(int primaryCode, int[] keyCodes) { + if (isInputViewShown()) { + if (mInputView.isShifted()) { + primaryCode = Character.toUpperCase(primaryCode); + } + } + + mComposing.append((char) primaryCode); + getCurrentInputConnection().setComposingText(mComposing, 1); + updateShiftKeyState(getCurrentInputEditorInfo()); + + if (mPredictionOn) { + updateCandidates(); + } + } + + private void handleClose() { + commitTyped(getCurrentInputConnection()); + requestHideSelf(0); + mInputView.closing(); + } + + void handleChangeSourceLanguage(Language newSource) { + mLanguageKeyboard = + new TranslatorKeyboard(this, newSource.getKeyboardResource(), mModeId); + mSymbolsKeyboard.updateLanguageKeyLabels(); + mSymbolsShiftedKeyboard.updateLanguageKeyLabels(); + mInputView.setKeyboard(mLanguageKeyboard); + } + + void handleChangeTargetLanguage(Language newTarget) { + mLanguageKeyboard.updateLanguageKeyLabels(); + mSymbolsKeyboard.updateLanguageKeyLabels(); + mSymbolsShiftedKeyboard.updateLanguageKeyLabels(); + mInputView.invalidateAllKeys(); + } + + void handleSwitchLanguages() { + Language newSource = mTranslator.getSourceLanguage(); + mLanguageKeyboard = + new TranslatorKeyboard(this, newSource.getKeyboardResource(), mModeId); + mInputView.setKeyboard(mLanguageKeyboard); + } + + private void checkToggleCapsLock() { + long now = System.currentTimeMillis(); + if (mLastShiftTime + 800 > now) { + mCapsLock = !mCapsLock; + mLastShiftTime = 0; + } else { + mLastShiftTime = now; + } + } + + public void pickDefaultCandidate() { + pickSuggestionManually(0); + } + + public void pickSuggestionManually(int index) { + if (mCompletionOn && mCompletions != null && index >= 0 + && index < mCompletions.length) { + CompletionInfo ci = mCompletions[index]; + getCurrentInputConnection().commitCompletion(ci); + if (mCandidateView != null) { + mCandidateView.clear(); + } + updateShiftKeyState(getCurrentInputEditorInfo()); + } else if (mComposing.length() > 0) { + // If we were generating candidate suggestions for the current + // text, we would commit one of them here. But for this sample, + // we will just commit the current text. + commitTyped(getCurrentInputConnection()); + } + } + + public void swipeRight() { + if (mCompletionOn) { + pickDefaultCandidate(); + } + } + + public void swipeLeft() { + handleBackspace(); + } + + public void swipeDown() { + handleClose(); + } + + public void swipeUp() { + } + + public void onPress(int primaryCode) { + } + + public void onRelease(int primaryCode) { + } +} diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorKeyboard.java b/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorKeyboard.java new file mode 100644 index 000000000..cd29d6914 --- /dev/null +++ b/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorKeyboard.java @@ -0,0 +1,103 @@ +package org.grammaticalframework.ui.android; + +import java.util.Locale; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.view.inputmethod.EditorInfo; +import android.inputmethodservice.Keyboard; + +public class TranslatorKeyboard extends Keyboard { + + private Key mEnterKey; + private Key mSourceLanguageKey; + private Key mTargetLanguageKey; + + static final int KEYCODE_SOURCE_LANGUAGE = -100; + static final int KEYCODE_TARGET_LANGUAGE = -200; + static final int MAX_LANGUAGE_KEYCODES = 99; + + private Translator mTranslator; + + public TranslatorKeyboard(Context context, int xmlLayoutResId, int modeId) { + super(context, xmlLayoutResId, modeId); + + mTranslator = ((GFTranslator) context.getApplicationContext()).getTranslator(); + updateLanguageKeyLabels(); + } + + public void updateLanguageKeyLabels() { + if (mSourceLanguageKey != null) + mSourceLanguageKey.label = getLanguageKeyLabel(mTranslator.getSourceLanguage()); + + if (mTargetLanguageKey != null) + mTargetLanguageKey.label = getLanguageKeyLabel(mTranslator.getTargetLanguage()); + } + + public static String getLanguageKeyLabel(Language lang) { + return + LocaleUtils.parseJavaLocale(lang.getLangCode(), Locale.getDefault()) + .getISO3Language(); + } + + @Override + protected Key createKeyFromXml(Resources res, Row parent, int x, int y, + XmlResourceParser parser) { + Key key = new Key(res, parent, x, y, parser); + if (key.codes[0] == 10) { + mEnterKey = key; + } else if (key.codes[0] == KEYCODE_SOURCE_LANGUAGE) { + mSourceLanguageKey = key; + } else if (key.codes[0] == KEYCODE_TARGET_LANGUAGE) { + mTargetLanguageKey = key; + } + return key; + } + + /** + * This looks at the ime options given by the current editor, to set the + * appropriate label on the keyboard's enter key (if it has one). + */ + void setImeOptions(Resources res, int options) { + if (mEnterKey == null) { + return; + } + + switch (options&(EditorInfo.IME_MASK_ACTION|EditorInfo.IME_FLAG_NO_ENTER_ACTION)) { + case EditorInfo.IME_ACTION_DONE: + mEnterKey.iconPreview = null; + mEnterKey.icon = null; + mEnterKey.label = res.getText(R.string.label_done_key); + break; + case EditorInfo.IME_ACTION_GO: + mEnterKey.iconPreview = null; + mEnterKey.icon = null; + mEnterKey.label = res.getText(R.string.label_go_key); + break; + case EditorInfo.IME_ACTION_NEXT: + mEnterKey.iconPreview = null; + mEnterKey.icon = null; + mEnterKey.label = res.getText(R.string.label_next_key); + break; + case EditorInfo.IME_ACTION_PREVIOUS: + mEnterKey.iconPreview = null; + mEnterKey.icon = null; + mEnterKey.label = res.getText(R.string.label_previous_key); + break; + case EditorInfo.IME_ACTION_SEARCH: + mEnterKey.icon = res.getDrawable(R.drawable.sym_keyboard_search); + mEnterKey.label = null; + break; + case EditorInfo.IME_ACTION_SEND: + mEnterKey.iconPreview = null; + mEnterKey.icon = null; + mEnterKey.label = res.getText(R.string.label_send_key); + break; + default: + mEnterKey.icon = res.getDrawable(R.drawable.sym_keyboard_return); + mEnterKey.label = null; + break; + } + } +}
\ No newline at end of file diff --git a/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorKeyboardView.java b/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorKeyboardView.java new file mode 100644 index 000000000..6bca2ad59 --- /dev/null +++ b/src/ui/android/src/org/grammaticalframework/ui/android/TranslatorKeyboardView.java @@ -0,0 +1,113 @@ +package org.grammaticalframework.ui.android; + +import java.util.Locale; + +import org.grammaticalframework.ui.android.TranslatorKeyboard; + +import android.content.Context; +import android.inputmethodservice.Keyboard.Key; +import android.inputmethodservice.KeyboardView; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +public class TranslatorKeyboardView extends KeyboardView { + + private Translator mTranslator; + + public TranslatorKeyboardView(Context context, AttributeSet attrs) { + super(context, attrs); + mTranslator = ((GFTranslator) context.getApplicationContext()).getTranslator(); + } + + public TranslatorKeyboardView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + mTranslator = ((GFTranslator) context.getApplicationContext()).getTranslator(); + } + + private PopupWindow mLanguagesPopup = null; + private Key mLanguagesKey = null; + + private void showLanguageOptions(Key popupKey) { + if (mLanguagesPopup == null) { + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + LinearLayout popupContainer = (LinearLayout) + inflater.inflate(R.layout.keyboard_languages_options, null); + + int index = 0; + for (Language lang : mTranslator.getAvailableLanguages()) { + Button item = new Button(getContext()); + item.setText(TranslatorKeyboard.getLanguageKeyLabel(lang)); + item.setTag(index); + item.setOnClickListener(this); + popupContainer.addView(item, index++); + } + + popupContainer.measure( + MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), + MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); + + mLanguagesPopup = new PopupWindow(getContext()); + mLanguagesPopup.setWidth(popupContainer.getMeasuredWidth()); + mLanguagesPopup.setHeight(popupContainer.getMeasuredHeight()); + mLanguagesPopup.setContentView(popupContainer); + + int[] windowOffset = new int[2]; + getLocationInWindow(windowOffset); + int popupX = popupKey.x + popupKey.width - popupContainer.getMeasuredWidth(); + int popupY = popupKey.y - popupContainer.getMeasuredHeight(); + final int x = popupX + popupContainer.getPaddingRight() + windowOffset[0]; + final int y = popupY + popupContainer.getPaddingBottom() + windowOffset[1]; + mLanguagesPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y); + + View closeButton = popupContainer.findViewById(R.id.closeButton); + if (closeButton != null) closeButton.setOnClickListener(this); + } + + mLanguagesKey = popupKey; + } + + private void dismissLanguages() { + if (mLanguagesPopup != null) { + mLanguagesPopup.dismiss(); + mLanguagesPopup = null; + mLanguagesKey = null; + } + } + + @Override + public void onClick(View v) { + super.onClick(v); + + if (v.getTag() != null) { + if (mLanguagesKey.codes[0] == TranslatorKeyboard.KEYCODE_SOURCE_LANGUAGE || + mLanguagesKey.codes[0] == TranslatorKeyboard.KEYCODE_TARGET_LANGUAGE) { + int keyCode = mLanguagesKey.codes[0] - ((Integer) v.getTag()) - 1; + getOnKeyboardActionListener().onKey(keyCode, new int[] {keyCode}); + } + } + + dismissLanguages(); + } + + public void closing() { + super.closing(); + dismissLanguages(); + } + + @Override + protected boolean onLongPress(Key key) { + if (key.codes[0] == TranslatorKeyboard.KEYCODE_SOURCE_LANGUAGE || + key.codes[0] == TranslatorKeyboard.KEYCODE_TARGET_LANGUAGE) { + showLanguageOptions(key); + return true; + } else { + return super.onLongPress(key); + } + } +} |
