abouttreesummaryrefslogcommitdiff
path: root/srcs
diff options
context:
space:
mode:
Diffstat (limited to 'srcs')
-rw-r--r--srcs/juloo.keyboard2/Config.java5
-rw-r--r--srcs/juloo.keyboard2/Keyboard2.java27
-rw-r--r--srcs/juloo.keyboard2/StringUtils.java12
-rw-r--r--srcs/juloo.keyboard2/Utils.java30
-rw-r--r--srcs/juloo.keyboard2/VoiceImeSwitcher.java143
5 files changed, 182 insertions, 35 deletions
diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java
index 31fe0b0..f8b2070 100644
--- a/srcs/juloo.keyboard2/Config.java
+++ b/srcs/juloo.keyboard2/Config.java
@@ -389,6 +389,11 @@ final class Config
return _globalConfig;
}
+ public static SharedPreferences globalPrefs()
+ {
+ return _globalConfig._prefs;
+ }
+
public static interface IKeyEventHandler
{
public void key_down(KeyValue value, boolean is_swipe);
diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java
index d045396..0e91f8c 100644
--- a/srcs/juloo.keyboard2/Keyboard2.java
+++ b/srcs/juloo.keyboard2/Keyboard2.java
@@ -151,7 +151,7 @@ public class Keyboard2 extends InputMethodService
_config.shouldOfferSwitchingToNextInputMethod = true;
else
_config.shouldOfferSwitchingToNextInputMethod = shouldOfferSwitchingToNextInputMethod();
- _config.shouldOfferVoiceTyping = (get_voice_typing_im(imm) != null);
+ _config.shouldOfferVoiceTyping = true;
KeyboardData default_layout = null;
_config.extra_keys_subtype = null;
if (VERSION.SDK_INT >= 12)
@@ -224,20 +224,6 @@ public class Keyboard2 extends InputMethodService
_keyboardView.reset();
}
- /** Returns the id and subtype of the voice typing IM. Returns [null] if none
- is installed or if the feature is unsupported. */
- SimpleEntry<String, InputMethodSubtype> get_voice_typing_im(InputMethodManager imm)
- {
- if (VERSION.SDK_INT < 11) // Due to InputMethodSubtype
- return null;
- for (InputMethodInfo im : imm.getEnabledInputMethodList())
- for (InputMethodSubtype imst : imm.getEnabledInputMethodSubtypeList(im, true))
- // Switch to the first IM that has a subtype of this mode
- if (imst.getMode().equals("voice"))
- return new SimpleEntry(im.getId(), imst);
- return null;
- }
-
private KeyboardData refresh_special_layout(EditorInfo info)
{
switch (info.inputType & InputType.TYPE_MASK_CLASS)
@@ -433,14 +419,9 @@ public class Keyboard2 extends InputMethodService
break;
case SWITCH_VOICE_TYPING:
- SimpleEntry<String, InputMethodSubtype> im = get_voice_typing_im(get_imm());
- if (im == null)
- return;
- // Best-effort. Good enough for triggering Google's voice typing.
- if (VERSION.SDK_INT < 28)
- switchInputMethod(im.getKey());
- else
- switchInputMethod(im.getKey(), im.getValue());
+ if (!VoiceImeSwitcher.switch_to_voice_ime(Keyboard2.this, get_imm(),
+ Config.globalPrefs()))
+ _config.shouldOfferVoiceTyping = false;
break;
}
}
diff --git a/srcs/juloo.keyboard2/StringUtils.java b/srcs/juloo.keyboard2/StringUtils.java
deleted file mode 100644
index 2994509..0000000
--- a/srcs/juloo.keyboard2/StringUtils.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package juloo.keyboard2;
-
-final class Utils
-{
- /** Turn the first letter of a string uppercase. */
- public static String capitalize_string(String s)
- {
- // Make sure not to cut a code point in half
- int i = s.offsetByCodePoints(0, 1);
- return s.substring(0, i).toUpperCase() + s.substring(i);
- }
-}
diff --git a/srcs/juloo.keyboard2/Utils.java b/srcs/juloo.keyboard2/Utils.java
new file mode 100644
index 0000000..cd28dfa
--- /dev/null
+++ b/srcs/juloo.keyboard2/Utils.java
@@ -0,0 +1,30 @@
+package juloo.keyboard2;
+
+import android.app.AlertDialog;
+import android.os.IBinder;
+import android.view.Window;
+import android.view.WindowManager;
+
+class Utils
+{
+ /** Turn the first letter of a string uppercase. */
+ public static String capitalize_string(String s)
+ {
+ // Make sure not to cut a code point in half
+ int i = s.offsetByCodePoints(0, 1);
+ return s.substring(0, i).toUpperCase() + s.substring(i);
+ }
+
+ /** Like [dialog.show()] but properly configure layout params when called
+ from an IME. [token] is the input view's [getWindowToken()]. */
+ public static void show_dialog_on_ime(AlertDialog dialog, IBinder token)
+ {
+ Window win = dialog.getWindow();
+ WindowManager.LayoutParams lp = win.getAttributes();
+ lp.token = token;
+ lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+ win.setAttributes(lp);
+ win.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ dialog.show();
+ }
+}
diff --git a/srcs/juloo.keyboard2/VoiceImeSwitcher.java b/srcs/juloo.keyboard2/VoiceImeSwitcher.java
new file mode 100644
index 0000000..7ddda5e
--- /dev/null
+++ b/srcs/juloo.keyboard2/VoiceImeSwitcher.java
@@ -0,0 +1,143 @@
+package juloo.keyboard2;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.inputmethodservice.InputMethodService;
+import android.os.Build.VERSION;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.widget.ArrayAdapter;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.List;
+
+class VoiceImeSwitcher
+{
+ static final String PREF_LAST_USED = "voice_ime_last_used";
+ static final String PREF_KNOWN_IMES = "voice_ime_known";
+
+ /** Switch to the voice ime. This might open a chooser popup. Preferences are
+ used to store the last selected voice ime and to detect whether the
+ chooser popup must be shown. Returns [false] if the detection failed and
+ is unlikely to succeed. */
+ public static boolean switch_to_voice_ime(InputMethodService ims,
+ InputMethodManager imm, SharedPreferences prefs)
+ {
+ if (VERSION.SDK_INT < 11) // Due to InputMethodSubtype
+ return false;
+ List<IME> imes = get_voice_ime_list(imm);
+ String last_used = prefs.getString(PREF_LAST_USED, null);
+ String last_known_imes = prefs.getString(PREF_KNOWN_IMES, null);
+ IME last_used_ime = get_ime_by_id(imes, last_used);
+ if (imes.size() == 0)
+ return false;
+ if (last_used == null || last_known_imes == null || last_used_ime == null
+ || !last_known_imes.equals(serialize_ime_ids(imes)))
+ choose_voice_ime_and_update_prefs(ims, prefs, imes);
+ else
+ switch_input_method(ims, last_used_ime);
+ return true;
+ }
+
+ /** Show the voice IME chooser popup and switch to the selected IME.
+ Preferences are updated so that future calls to [switch_to_voice_ime]
+ switch to the newly selected IME. */
+ static void choose_voice_ime_and_update_prefs(final InputMethodService ims,
+ final SharedPreferences prefs, final List<IME> imes)
+ {
+ List<String> ime_display_names = get_ime_display_names(ims, imes);
+ ArrayAdapter layouts = new ArrayAdapter(ims, android.R.layout.simple_list_item_1, ime_display_names);
+ AlertDialog dialog = new AlertDialog.Builder(ims)
+ .setAdapter(layouts, new DialogInterface.OnClickListener(){
+ public void onClick(DialogInterface _dialog, int which)
+ {
+ IME selected = imes.get(which);
+ prefs.edit()
+ .putString(PREF_LAST_USED, selected.get_id())
+ .putString(PREF_KNOWN_IMES, serialize_ime_ids(imes))
+ .commit();
+ switch_input_method(ims, selected);
+ }
+ })
+ .create();
+ Utils.show_dialog_on_ime(dialog, ims.getWindow().getWindow().getDecorView().getWindowToken());
+ }
+
+ static void switch_input_method(InputMethodService ims, IME ime)
+ {
+ if (VERSION.SDK_INT < 28)
+ ims.switchInputMethod(ime.get_id());
+ else
+ ims.switchInputMethod(ime.get_id(), ime.subtype);
+ }
+
+ static IME get_ime_by_id(List<IME> imes, String id)
+ {
+ if (id != null)
+ for (IME ime : imes)
+ if (ime.get_id().equals(id))
+ return ime;
+ return null;
+ }
+
+ static List<String> get_ime_display_names(InputMethodService ims, List<IME> imes)
+ {
+ List<String> names = new ArrayList<String>();
+ for (IME ime : imes)
+ names.add(ime.get_display_name(ims));
+ return names;
+ }
+
+ static List<IME> get_voice_ime_list(InputMethodManager imm)
+ {
+ List<IME> imes = new ArrayList<IME>();
+ for (InputMethodInfo im : imm.getEnabledInputMethodList())
+ for (InputMethodSubtype imst : imm.getEnabledInputMethodSubtypeList(im, true))
+ if (imst.getMode().equals("voice"))
+ imes.add(new IME(im, imst));
+ return imes;
+ }
+
+ /** The chooser popup is shown whether this string changes. */
+ static String serialize_ime_ids(List<IME> imes)
+ {
+ StringBuilder b = new StringBuilder();
+ for (IME ime : imes)
+ {
+ b.append(ime.get_id());
+ b.append(',');
+ }
+ return b.toString();
+ }
+
+ static class IME
+ {
+ public final InputMethodInfo im;
+ public final InputMethodSubtype subtype;
+
+ IME(InputMethodInfo im_, InputMethodSubtype st)
+ {
+ im = im_;
+ subtype = st;
+ }
+
+ String get_id() { return im.getId(); }
+
+ /** Localised display name. */
+ String get_display_name(Context ctx)
+ {
+ String subtype_name = "";
+ if (VERSION.SDK_INT >= 14)
+ {
+ subtype_name = subtype.getDisplayName(ctx, im.getPackageName(), null).toString();
+ if (!subtype_name.equals(""))
+ subtype_name = " - " + subtype_name;
+ }
+ return im.loadLabel(ctx.getPackageManager()).toString() + subtype_name;
+ }
+ }
+}