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()
.