1

I have made a custom keyboard for Android. Unfortunately, on Samsung devices that have hardware buttons users cannot switch to my keyboard from the default keyboard without setting my keyboard as the default keyboard. This is a problem because my keyboard is meant to be used for a few specific tasks.

Is there anytype of service that allows me to display a "switch keyboard" notification in the pull-down notification bar if the system is not already displaying one?

Foobar
  • 7,458
  • 16
  • 81
  • 161
  • AFAIK, there still is no definitive way to detect the soft keyboard state/change. If anyone knows differently, I hope they comment. You might consider using an ongoing `Notification` (for which you wouldn't really need a `Service`), and allow the user to enable/disable that as they need. – Mike M. Sep 26 '16 at 22:59
  • Ok, I won't bother detecting it the keyboard's state then. Is it still possible to provide a notification that allows the user to switch keyboards? – Foobar Sep 30 '16 at 21:26
  • Sure. You could create an ongoing `Notification` - by calling `setOngoing(true)` on its `Builder` - use a `getBroadcast()` `PendingIntent` for its content `Intent`, and in the `BroadcastReceiver`'s `onReceive()` method, call the `InputMethodManager#showInputMethodPicker()`, like shown in [the post I linked](http://stackoverflow.com/questions/35376419/how-to-mimic-google-keyboards-spacebar-long-press-functionality-for-switching-k) previously. You could issue the `Notification` at boot with a `BOOT_COMPLETED` Receiver, and use a regular setting to allow the user to enable/disable it as necessary – Mike M. Sep 30 '16 at 21:37
  • Lemme know if you'd like some examples. I use a similar setup for a personal app I have in the tray all the time. – Mike M. Sep 30 '16 at 21:55
  • 1
    Examples would be great. – Foobar Oct 05 '16 at 23:04

1 Answers1

1

A possible workaround for your situation would be to offer the user a Notification that they can enable/disable as needed. This could be made persistent, so it can't be accidentally dismissed, and, with a boot Receiver, it can be setup to show automatically even after system restarts.

My comments kind of implied that we'd need two Receivers - one for the boot broadcast, and one for the Notification broadcast - but we can actually do this with just one. We can then package all of the other necessary functionalities into that same class. For example:

public class ImeNotificationManager extends BroadcastReceiver {

    private static final String PREF_KEY_BOOT_SHOW = "show_at_boot";
    private static final int NOTIFICATION_ID = 137;

    @Override
    public void onReceive(Context context, Intent intent) {
        // If we're booting, check Preferences to see if we should show
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            if (isShowAtBoot(context)) {
                showNotification(context, true);
            }
        }
        // If the action is not BOOT_COMPLETED, then we've called our Receiver
        // explicitly ourselves from the Notification, so we show the picker
        else {
            showImePicker(context);
        }
    }

    public static void setEnabled(Context context, boolean enabled) {
        setShowAtBoot(context, enabled);
        showNotification(context, enabled);
    }

    public static void setShowAtBoot(Context context, boolean showAtBoot) {
        PreferenceManager.getDefaultSharedPreferences(context)
            .edit().putBoolean(PREF_KEY_BOOT_SHOW, showAtBoot).apply();
    }

    public static boolean isShowAtBoot(Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context)
            .getBoolean(PREF_KEY_BOOT_SHOW, false);
    }

    public static void showNotification(Context context, boolean show) {
        NotificationManagerCompat nm = NotificationManagerCompat.from(context);

        if (show) {
            Intent i = new Intent(context, ImeNotificationManager.class);
            PendingIntent pi = PendingIntent.getBroadcast(context, 0, i, 0);

            NotificationCompat.Builder builder =
                new NotificationCompat.Builder(context)
                    .setContentTitle("Choose input method")
                    .setContentText("Show IME picker")
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setContentIntent(pi)
                    .setOngoing(true) // This makes it persistent
                    .setWhen(0);      // This orders it toward the top

            nm.notify(NOTIFICATION_ID, builder.build());
        }
        else {
            nm.cancel(NOTIFICATION_ID);
        }
    }

    public static void showImePicker(Context context) {
        InputMethodManager imm = (InputMethodManager)
            context.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.showInputMethodPicker();
    }
}

To get the Notification and boot broadcasts, we need to register our class as a <receiver> in the manifest. For the boot broadcast, we also need the following permission, listed outside of the <application> tags.

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

The <receiver> element goes inside those tags.

<receiver android:name=".ImeNotificationManager"
          android:exported="false">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

All of the methods that you'd need to call directly are static, and can be used wherever you have access to a Context in your app. For example, to both show the Notification immediately, and enable the show-at-boot function in one shot:

ImeNotificationManager.setEnabled(context, true);

To disable both, simply call the above method with false as the second argument. The showNotification() and setShowAtBoot() methods are also available to handle each independently, and you can show the picker yourself directly with showImePicker().

Mike M.
  • 38,532
  • 8
  • 99
  • 95
  • after adding setChannelId as mentioned [here](https://stackoverflow.com/a/16448278/11337921), notification correctly shows, clicking it shows IME picker when my app is in foreground, but does not when the app is minimized even though the line `imm.showInputMethodPicker()` is executed, any ideas to why and how to solve it? – nicolauscg Aug 21 '20 at 11:00
  • @NicolausChristian Hmm, not sure. Which version of Android are you testing on? – Mike M. Aug 22 '20 at 18:03