I have created an android custom keyboard. After pressing a button on it, I'd like it to change the keyboard back to the previous keyboard, presumable using InputMethodManager.setInputMethod(IBinder token, String id);
However, I can't work out where to get the token from - using getCurrentInputBinding().getConnectionToken() doesn't work.
Does anyone know where to find the token?
Thanks,
Ed
Turns out that the switchInputMethod(String id) method works a treat - no need for that token.
You get the token from the view by view.getWindowToken().
You can use this Method to get Token and activate last used Keyboard
private fun switchToLastKeyboard() {
try {
val imm: InputMethodManager =
this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val token = this.window.window!!.attributes.token
//imm.setInputMethod(token, LATIN);
imm.switchToLastInputMethod(token)
} catch (t: Throwable) { // java.lang.NoSuchMethodError if API_level<11
Log.i("TAG", "onCreateInputView: Throwable " + t.message)
}
}
Related
Is there a way to capture the back button click listener from Google Places Address Search (AutocompleteSupportFragment)
val btnBackClick =
autocompleteFragment?.view?.findViewById(R.id.places_autocomplete_back_button) as androidx.appcompat.widget.AppCompatImageButton
btnBackClick.setOnClickListener {
Log.e("AutoComplete", "Address Search Back")
}
Tried this leading to crash "java.lang.IllegalStateException: Places must be initialized."
I've tried to get the back button the same way you did on my end, but you are right, that View returns null even though it apparently exists: R.id.places_autocomplete_back_button.
Update: At the moment it is not currently possible to get this View, so I recommend you file a feature request for this in Google's Issue Tracker in case Google Engineers are able to consider adding this capability.
Hope this helps!
I had the same problem. I couldn't get a reference through neither the back button of the AutocompleteSupportFragment / onBackPressed listener / onBackPressedDispatcher from the activity, so I went on and used the error status instead.
// Assuming "autocompleteFragment" is the view name in your XML file
val fragment = supportFragmentManager.findFragmentById(R.id.autocompleteFragment) as AutocompleteSupportFragment
// ...
fragment.setOnPlaceSelectedListener(object : PlaceSelectionListener {
override fun onPlaceSelected(place: Place) {
// Handle the result like usual
}
override fun onError(status: Status) {
// Check if the user tapped the back button
if (status == Status.RESULT_CANCELED) {
// Do what you want to do when back button is pressed
}
}
}
Technically a workaround, but it does capture the back button flow.
I am using BiometricPrompt in my application. It works well and shows the dialog when call the authenticate() method. But this dialog gets closing when I click outside the dialog. How to prevent it? How to make BiometricPrompt's dialog non-cancelable? Here is no method like biometricPrompt.setCancelable(false).
BiometricPrompt does not allow that. So you won't be able to make the system-provided biometric prompt non-cancelable. But you can detect whenever user cancels the dialog.
So an option would be, to show again the biometric prompt after user cancel it (which I think would be a bad user experience) or use alternate user authentication:
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
if (errorCode == BiometricConstants.ERROR_USER_CANCELED) {
// User canceled the operation
// you can either show the dialog again here
// or use alternate authentication (e.g. a password) - recommended way
}
}
check it out
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
supportFragmentManager.fragments.forEach {
if(it is DialogFragment) {
it.dialog?.setCanceledOnTouchOutside(false)
}
}
}
There are some devices that still have this issue. An work around will be to get root view and add an overlay view with clickable method set to false.
ViewGroup viewGroup = ((ViewGroup) yourActivity.findViewById(android.R.id.content)).getChildAt(0);
//create your view
Display display = mActivity.getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
View view = new View(yourActivity);
view.setId(R.id.overlay_view);
view.setLayoutParams(new ViewGroup.LayoutParams(size.x, size.y));
view.setBackgroundColor(ContextCompat.getColor(yourActivity, R.color.black));
view.setOnClickListener(v -> {
//do nothing prevent click under this overlay
});
//add your view on top of the screen
viewGroup.addView(view);
//call your biometric dialog
....
//on callbacks even if it is error or success call remove view
viewGroup.removeView(view);
You have to use the version 1.0.0-beta01 or later.
Now it is the default behavior:
Touches outside no longer cancel authentication. Back button cancel authentication still.
You can see the changelog:
Changed behavior to not allow BiometricPrompt to be cancelled by a touch event outside the prompt.
You can check also the rewiew report.
No new API.
I'm trying to create a listener for the clear button that comes from google's Place Autocomplete API. i called my clearButton() method in my fragment's onViewCreated method
clearButton()
placeAutocompleteFragment?.view?.findViewById<View>(R.id.place_autocomplete_clear_button)
?.setOnClickListener {
View.OnClickListener {
Log.d(TAG, "Cleared")
it?.findViewById<EditText>(R.id.place_autocomplete_search_input)?.setText("")
it?.visibility = View.GONE
}
}
now when i click on the clear button icon, the text doesn't get erased, nothing happens. I can still type in a new location though, but i can't clear it. my Log.d isn't getting displayed either.
I don't have android studio on this machine now to try, but I guess you can do something like
place_autocomplete_clear_button.onClick { place_autocomplete_search_input.text = "" }
where place_autocomplete_clear_button can be static import and onClick might be from anko
Figured it out. I had the method calls set up all wrong.
Here's how it should look like:
private fun clearButton() {
placeAutocompleteFragment?.view?.findViewById<View>(R.id.place_autocomplete_clear_button)?.setOnClickListener {
Log.d(TAG, "Cleared Button Clicked")
it.visibility = View.GONE
//do something
}
}
I am writing a Xamarin Forms app (.net standard 2.0). Currently it is only being developed for Android but it may be released for other OSs in future. The scenario I am trying to manage is this:
The user goes into a ContentPage with a single Entry
I give the Entry focus by using native Android code in a custom renderer:
if (e.NewElement != null && e.NewElement is CustomEntry)
{
CustomEntry customEntry = (CustomEntry)e.NewElement;
if(customEntry.GiveFocus)
{
//this messes up the onback behaviour - you have to press onback twice to exit the screen, once to get out of the hidden SIP
Control.RequestFocus();
}
}
I do not want the soft keyboard to pop up automatically. Therefore I have added the below line to the OnCreate of the MainActivity:
Window.SetSoftInputMode(SoftInput.StateAlwaysHidden);
The reason I am requesting focus in the custom renderer and not in the Xamarin Forms Entry is I could see the keyboard popup and then immediately disappear when I requested it in the Xamarin Forms control. I don't want the keyboard to appear as this app will be primarily used by users of industrial devices with a hardware keyboard, but the entry will need to have focus as the users will want to enter text into it straight away.
My problem is the user has to press the back button twice to exit the ContentPage in this scenario. Once to get out of the hidden keyboard (and the Entry loses focus) and then again to exit the page. I want to avoid this - they should be able to exit the page with only one click when the keyboard is hidden. Does anyone know how to resolve this? I have tried overriding OnKeyPreIme in the custom renderer as other answers have suggested but it doesn't appear to detect the back click.
You can use Hide Keyboard method when your entry get focused. It might solved your problem.
public interface IKeyboardHelper
{
void HideKeyboard();
}
For Android use :
public class DroidKeyboardHelper : IKeyboardHelper
{
public void HideKeyboard()
{
var context = Forms.Context;
var inputMethodManager = context.GetSystemService(Context.InputMethodService) as InputMethodManager;
if (inputMethodManager != null && context is Activity)
{
var activity = context as Activity;
var token = activity.CurrentFocus?.WindowToken;
inputMethodManager.HideSoftInputFromWindow(token, HideSoftInputFlags.None);
activity.Window.DecorView.ClearFocus();
}
}
}
For iOS :
public class iOSKeyboardHelper : IKeyboardHelper
{
public void HideKeyboard()
{
UIApplication.SharedApplication.KeyWindow.EndEditing(true);
}
}
Use Dependency Injection and call this method while your entry get focused.
Try to use below method for handling back button pressed event.
protected override bool OnBackButtonPressed()
{
// you can handle back button pressed event in Xamarin forms page
return base.OnBackButtonPressed();
}
I've (finally) worked it out. The key is not to override OnKeyPreIME but DispatchKeyEventPreIme instead. This allows you to intercept the 'Back' press. So, in my CustomRenderer I added this method:
public override bool DispatchKeyEventPreIme(KeyEvent e)
{
//if this is back press and the sip is not visible then we need to call the 'OnBack' method at the view model level
if(!SIPVisibleListener.IsSIPVisible && e.KeyCode == Keycode.Back)
{
if(XamarinFormsControl != null && XamarinFormsControl is IOnBackHandler)
{
((IOnBackHandler)XamarinFormsControl).GoBack();
}
}
return base.DispatchKeyEventPreIme(e);
}
IOnBackHandler is an interface I created to handle the back key press. SIPVisibleListener is based on an answer to this question: How do I Detect if Software Keyboard is Visible on Android Device?
Hopefully this will help someone.
I want to test keyboard visibility when an activity calls onCreate() and onResume().
How can i test whether or not the keyboard is shown using espresso?
I know, that the question is old enough, but it doesn't have any accepted answer though.
In our UI tests we use this method, which uses some shell commands:
/**
* This method works like a charm
*
* SAMPLE CMD OUTPUT:
* mShowRequested=true mShowExplicitlyRequested=true mShowForced=false mInputShown=true
*/
fun isKeyboardOpenedShellCheck(): Boolean {
val checkKeyboardCmd = "dumpsys input_method | grep mInputShown"
try {
return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
.executeShellCommand(checkKeyboardCmd).contains("mInputShown=true")
} catch (e: IOException) {
throw RuntimeException("Keyboard check failed", e)
}
}
Hope, it'll be useful for someone
fun isKeyboardShown(): Boolean {
val inputMethodManager = InstrumentationRegistry.getInstrumentation().targetContext.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
return inputMethodManager.isAcceptingText
}
found at Google groups
another trick could be checking for the visibility of a view that you know is going to be covered when the keyboard is showing. don't forget to take animations into consideration...
instrumentation testing using espresso and hamcrest for the NOT matcher something like:
//make sure keyboard is visible by clicking on an edit text component
ViewInteraction v = onView(withId(R.id.editText));
ViewInteraction v2 = onView(withId(R.id.componentVisibleBeforeKeyboardIsShown));
v2.check(matches(isDisplayed()));
v.perform(click());
//add a small delay because of the showing keyboard animation
SystemClock.sleep(500);
v2.check(matches(not(isDisplayed())));
hideKeyboardMethod();
//add a small delay because of the hiding keyboard animation
SystemClock.sleep(500);
v2.check(matches(isDisplayed()));
This works for me.
private boolean isSoftKeyboardShown() {
final InputMethodManager imm = (InputMethodManager) getActivityInstance()
.getSystemService(Context.INPUT_METHOD_SERVICE);
return imm.isAcceptingText();
}
Java version of #igork's answer.
This method is working for me
val isKeyboardOpened: Boolean
get() {
for (window in InstrumentationRegistry.getInstrumentation().uiAutomation.windows) {
if (window.type == AccessibilityWindowInfo.TYPE_INPUT_METHOD) {
return true
}
}
return false
}