ionic-team / cordova-plugin-ionic-keyboard

Keyboard Plugin for Cordova
Other
192 stars 177 forks source link

resizeOnFullScreen makes app extend below Navigation Bar #117

Open dlee opened 4 years ago

dlee commented 4 years ago

When using resizeOnFullScreen on Android, it makes the app extend below the Navigation Bar.

/cc @jcesarmobile

fredrikhaggbom commented 4 years ago

Same problem for me. When using resizeOnFullScreen=true the view is pushed up when keyboard is show, but my bottom tab bar is hidden beneath the Android OS navigation bar (back, home and overview buttons). If using resizeOnFullScreen=false, the tab bar is correctly place above the navigation bar, but the view is not pushed up when keyboard is showing.

Ionic info:

Ionic:

   ionic (Ionic CLI)  : 4.10.3 (/usr/local/lib/node_modules/ionic)
   Ionic Framework    : ionic-angular 3.9.2
   @ionic/app-scripts : 3.2.4

Cordova:

   cordova (Cordova CLI) : 9.0.0 (cordova-lib@9.0.1)
   Cordova Platforms     : android 6.4.0, browser 5.0.4, ios 5.0.1
   Cordova Plugins       : cordova-plugin-ionic-keyboard 2.2.0, cordova-plugin-ionic-webview 3.1.2, (and 16 other plugins)

System:

   Android SDK Tools : 26.1.1 (/Users/user/Library/Android/sdk)
   ios-sim           : 8.0.2
   NodeJS            : v10.15.3 (/usr/local/bin/node)
   npm               : 6.11.3
   OS                : macOS
   Xcode             : Xcode 11.1 Build version 11A1027
BigBerny commented 4 years ago

Same here. It would be great if this could be fixed.

Is there a way to modify "resizeOnFullScreen" while running the app? I need this feature only in one place and there's no bottom tab bar.

AhSem commented 4 years ago

Having the same issue. Anyone has a workaround? I am also looking for a way to change "resizeOnFullScreen" in app.

yashp98 commented 4 years ago

same issue i face

brenobarnard commented 4 years ago

Any workaround for this, i'm facing the same issue here

Shneeball commented 4 years ago

Also getting this issue now, and setting this to true fixes the keyboard overlapping input fields for me on Android. Would be great if there was a workaround for this.

nopol10 commented 4 years ago

I made the following changes to CDVIonicKeyboard.java as a fix, it seems to work on my end (Oppo Reno Z with software navbar and Samsung Galaxy S7 with hardware navbar) but I don't know if it works for all cases/devices.

I used the solution here to obtain the navigation bar's height: https://stackoverflow.com/a/50775459/12117100

You might want to change the ViewCompat.setOnApplyWindowInsetsListener to rootView.setOnApplyWindowInsetsListener if your API level supports it.

Please test it out and see if it works (added lines are below the "Fix" comments):

public class CDVIonicKeyboard extends CordovaPlugin {
    private OnGlobalLayoutListener list;
    // ...
    // Fix
    private int navbarOffset = 0;

cordova.getThreadPool().execute(new Runnable() {
                public void run() {
                    // ...
                    FrameLayout content = (FrameLayout) cordova.getActivity().findViewById(android.R.id.content);
                    rootView = content.getRootView();
                    // Fix
                    ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
                        navbarOffset = insets.getSystemWindowInsetBottom();
                        return insets.consumeSystemWindowInsets();
                    });
// ...
                private void possiblyResizeChildOfContent() {
                        int usableHeightNow = computeUsableHeight();
                        if (usableHeightNow != usableHeightPrevious) {
                            // Fix: modified the next line
                            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight() - navbarOffset;
                            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
                            // ...
                        }
                }

I know next to nothing about Android development so if there are any mistakes do help to correct them, thanks!

XiaoQueXinggg commented 4 years ago

I made the following changes to CDVIonicKeyboard.java as a fix, it seems to work on my end (Oppo Reno Z with software navbar and Samsung Galaxy S7 with hardware navbar) but I don't know if it works for all cases/devices.

I used the solution here to obtain the navigation bar's height: https://stackoverflow.com/a/50775459/12117100

You might want to change the ViewCompat.setOnApplyWindowInsetsListener to View.setOnApplyWindowInsetsListener if your API level supports it.

Please test it out and see if it works (added lines are below the "Fix" comments):

public class CDVIonicKeyboard extends CordovaPlugin {
    private OnGlobalLayoutListener list;
    // ...
    // Fix
    private int navbarOffset = 0;

cordova.getThreadPool().execute(new Runnable() {
                public void run() {
                    // ...
                    FrameLayout content = (FrameLayout) cordova.getActivity().findViewById(android.R.id.content);
                    rootView = content.getRootView();
                    // Fix
                    ViewCompat.setOnApplyWindowInsetsListener(rootView, (v, insets) -> {
                        navbarOffset = insets.getSystemWindowInsetBottom();
                        return insets.consumeSystemWindowInsets();
                    });
// ...
                private void possiblyResizeChildOfContent() {
                        int usableHeightNow = computeUsableHeight();
                        if (usableHeightNow != usableHeightPrevious) {
                            // Fix: modified the next line
                            int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight() - navbarOffset;
                            int heightDifference = usableHeightSansKeyboard - usableHeightNow;
                            // ...
                        }
                }

I know next to nothing about Android development so if there are any mistakes do help to correct them, thanks!

hello,When i pack app ,log ViewCompat no exist and i change it to View, it also log error. I should change it to ?Thanks

nopol10 commented 4 years ago

Use rootView.setOnApplyWindowInsetsListener if you're not using ViewCompat, I'll edit my original message to make it clearer.

Follow the API here Android View reference

XiaoQueXinggg commented 4 years ago

hello.I try use rootView pack app,however it still log error as follows,Thanks for your work !!!OnApplyWindowInsetsListener �ҵ�: View,(v,insets)[...](); }

nopol10 commented 4 years ago

Can you paste the entire error, the error you pasted seems to be cut off and has some odd characters

ldittel commented 4 years ago

I made the following changes to CDVIonicKeyboard.java as a fix, it seems to work on my end (Oppo Reno Z with software navbar and Samsung Galaxy S7 with hardware navbar) but I don't know if it works for all cases/devices.

...

I know next to nothing about Android development so if there are any mistakes do help to correct them, thanks!

Thanks for this solution but it has one downside, it does not quite work in landscape orientation (leaves an ugly margin area where navbar used to be in portrait mode, and does not copy over the navbar background). At least on my OnePlus 3T with android 9.

Please see my fix that does not override the original onApplyWindowInsetsListener, gets the insets at init time and takes into account device orientation (android >21) to calculate the correct height.

Here is the complete CDVIonicKeyboard.java file, changes are marked with // FIX. For some reason it won't properly format as either code or comment so apologies for the broken formatting.

Anyway, hope it helps someone.

package io.ionic.keyboard;

import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaWebView; import org.apache.cordova.PluginResult; import org.apache.cordova.PluginResult.Status; import org.json.JSONArray; import org.json.JSONException;

import android.content.Context; import android.graphics.Rect; import android.util.DisplayMetrics; import android.view.View; import android.view.WindowInsets; import android.view.ViewTreeObserver; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.inputmethod.InputMethodManager;

// import additionally required classes for calculating screen height import android.view.Display; import android.graphics.Point; import android.os.Build; import android.widget.FrameLayout;

public class CDVIonicKeyboard extends CordovaPlugin { private OnGlobalLayoutListener list; private View rootView; private View mChildOfContent; private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams;

   /// FIX ADDED 
   private int navbarOffset = 0;
   ///

   public void initialize(CordovaInterface cordova, CordovaWebView webView) {
       super.initialize(cordova, webView);
   }

   public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
       if ("hide".equals(action)) {
           cordova.getThreadPool().execute(new Runnable() {
               public void run() {
                   //http://stackoverflow.com/a/7696791/1091751
                   InputMethodManager inputManager = (InputMethodManager) cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
                   View v = cordova.getActivity().getCurrentFocus();

                   if (v == null) {
                       callbackContext.error("No current focus");
                   } else {
                       inputManager.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
                       callbackContext.success(); // Thread-safe.
                   }
               }
           });
           return true;
       }
       if ("show".equals(action)) {
           cordova.getThreadPool().execute(new Runnable() {
               public void run() {
                   ((InputMethodManager) cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE)).toggleSoftInput(0, InputMethodManager.HIDE_IMPLICIT_ONLY);
                   callbackContext.success(); // Thread-safe.
               }
           });
           return true;
       }
       if ("init".equals(action)) {
           cordova.getThreadPool().execute(new Runnable() {
               public void run() {
                   //calculate density-independent pixels (dp)
                   //http://developer.android.com/guide/practices/screens_support.html
                   DisplayMetrics dm = new DisplayMetrics();
                   cordova.getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
                   final float density = dm.density;

                   //http://stackoverflow.com/a/4737265/1091751 detect if keyboard is showing
                   FrameLayout content = (FrameLayout) cordova.getActivity().findViewById(android.R.id.content);
                   rootView = content.getRootView();

                   /// FIX ADDED 
                   WindowInsets insets = rootView.getRootWindowInsets();
                   navbarOffset = insets.getSystemWindowInsetBottom();
                   ///

                   list = new OnGlobalLayoutListener() {
                       int previousHeightDiff = 0;
                       @Override
                       public void onGlobalLayout() {
                           boolean resize = preferences.getBoolean("resizeOnFullScreen", false);
                           if (resize) {
                               possiblyResizeChildOfContent();
                           }
                           Rect r = new Rect();
                           //r will be populated with the coordinates of your view that area still visible.
                           rootView.getWindowVisibleDisplayFrame(r);

                           PluginResult result;

                           // cache properties for later use
                           int rootViewHeight = rootView.getRootView().getHeight();
                           int resultBottom = r.bottom;

                           // calculate screen height differently for android versions >= 21: Lollipop 5.x, Marshmallow 6.x
                           //http://stackoverflow.com/a/29257533/3642890 beware of nexus 5
                           int screenHeight;

                           if (Build.VERSION.SDK_INT >= 21) {
                               Display display = cordova.getActivity().getWindowManager().getDefaultDisplay();
                               Point size = new Point();
                               display.getSize(size);
                               screenHeight = size.y;
                           } else {
                               screenHeight = rootViewHeight;
                           }

                           int heightDiff = screenHeight - resultBottom;

                           int pixelHeightDiff = (int)(heightDiff / density);
                           if (pixelHeightDiff > 100 && pixelHeightDiff != previousHeightDiff) { // if more than 100 pixels, its probably a keyboard...
                               String msg = "S" + Integer.toString(pixelHeightDiff);
                               result = new PluginResult(PluginResult.Status.OK, msg);
                               result.setKeepCallback(true);
                               callbackContext.sendPluginResult(result);
                           }
                           else if ( pixelHeightDiff != previousHeightDiff && ( previousHeightDiff - pixelHeightDiff ) > 100 ){
                               String msg = "H";
                               result = new PluginResult(PluginResult.Status.OK, msg);
                               result.setKeepCallback(true);
                               callbackContext.sendPluginResult(result);
                           }
                           previousHeightDiff = pixelHeightDiff;
                       }

                       /// FIX ADDED
                       private boolean isPortrait() {
                           if (Build.VERSION.SDK_INT >= 21) {
                               Display display = cordova.getActivity().getWindowManager().getDefaultDisplay();
                               int rotation = display.getRotation();
                               if (rotation == 0)
                                   return true;
                               else
                                   return false;
                           }
                           return true;
                       }
                       ///

                       private void possiblyResizeChildOfContent() {
                           int usableHeightNow = computeUsableHeight();
                           if (usableHeightNow != usableHeightPrevious) {
                               /// FIX MODIFIED 
                               int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
                               if (this.isPortrait()) 
                                   usableHeightSansKeyboard = usableHeightSansKeyboard - navbarOffset;
                               ///
                               int heightDifference = usableHeightSansKeyboard - usableHeightNow;
                               if (heightDifference > (usableHeightSansKeyboard/4)) {
                                   frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
                               } else {
                                   frameLayoutParams.height = usableHeightSansKeyboard;
                               }
                               mChildOfContent.requestLayout();
                               usableHeightPrevious = usableHeightNow;
                           }
                       }

                       private int computeUsableHeight() {
                           Rect r = new Rect();
                           mChildOfContent.getWindowVisibleDisplayFrame(r);
                           return (r.bottom - r.top);
                       }
                   };

                   mChildOfContent = content.getChildAt(0);
                   rootView.getViewTreeObserver().addOnGlobalLayoutListener(list);
                   frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
                   PluginResult dataResult = new PluginResult(PluginResult.Status.OK);
                   dataResult.setKeepCallback(true);
                   callbackContext.sendPluginResult(dataResult);
               }
           });
           return true;
       }
       return false;  // Returning false results in a "MethodNotFound" error.
   }

   @Override
   public void onDestroy() {
       rootView.getViewTreeObserver().removeOnGlobalLayoutListener(list);
   }

}

adrian-bueno commented 4 years ago

I have made this changes and it works well in my app (both in portrait and landscape mode):


// ...

private void possiblyResizeChildOfContent() {
    int usableHeightNow = computeUsableHeight();
    if (usableHeightNow != usableHeightPrevious) {
        // COMMENT THIS LINES
        // int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
        // int heightDifference = usableHeightSansKeyboard - usableHeightNow;
        // if (heightDifference > (usableHeightSansKeyboard/4)) {
        //     frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
        // } else {
        //     frameLayoutParams.height = usableHeightSansKeyboard;
        // }
        frameLayoutParams.height = usableHeightNow; // ADD THIS LINE
        mChildOfContent.requestLayout();
        usableHeightPrevious = usableHeightNow;
    }
}

// ...

Tested in OnePlus 6 (Android 10) and Huawei P Smart (Android 8.0.0)

I'm not an Android developer and I don't understand why the commented lines were added.

sagrawal31 commented 4 years ago

There is an Android bug that prevents the keyboard from resizing the WebView when the app is in full screen (i.e. if StatusBar plugin is used to hide the StatusBar). This setting, if set to true, add a workaround that resizes the WebView even when the app is in full screen.

It's strange that this flag is supposed to fix an Android bug but it itself introduced another bug which is way weird. 😃

By the way, we can also reproduce this in our fresh Ionic 5 app. Basically, when keyboard hides, the content in ion-footer got hidden behind the navigation bar.

If Ionic team wants a demo app to reproduce, we can provide that.

ysdoar commented 4 years ago

same problem this solution: rootView.setOnApplyWindowInsetsListener((v, insets) -> { navbarOffset = insets.getSystemWindowInsetBottom(); return insets.consumeSystemWindowInsets(); });

dlee commented 4 years ago

btw, here's what I've been using to work around this: https://github.com/SamDV-ich/cordova-plugin-ionic-keyboard-fixed

Hopefully the approach taken in that fork can be of help resolving this issue.

ponty-pilot commented 4 years ago

Hello! Did as ldittel

wrote but unfortunately a strip appears on many devices

screen

JayzeeHuang commented 4 years ago

got this at android device, ios works fine.

Screenshot_20200726_112720

Brendan92 commented 3 years ago

I still have this same issue, can someone please assist me as I have tried everything but no luck, here are some info:

Ionic:

Ionic CLI : 6.11.9 (/usr/local/lib/node_modules/@ionic/cli) Ionic Framework : @ionic/angular 5.3.3 @angular-devkit/build-angular : 0.901.12 @angular-devkit/schematics : 9.1.12 @angular/cli : 9.1.12 @ionic/angular-toolkit : 2.3.3

Cordova:

Cordova CLI : 10.0.0 Cordova Platforms : android 9.0.0, ios 5.1.1 Cordova Plugins : cordova-plugin-ionic-keyboard 2.2.0, cordova-plugin-ionic-webview 5.0.0, (and 19 other plugins)

Utility:

cordova-res : 0.15.1 native-run : 1.1.0

System:

Android SDK Tools : 26.1.1 (/Users/****/Library/Android/sdk) ios-deploy : 1.11.1 ios-sim : 8.0.2 NodeJS : v12.18.3 (/usr/local/bin/node) npm : 6.14.6 OS : macOS Catalina Xcode : Xcode 12.0.1 Build version 12A7300

LeonardFavioC commented 3 years ago

I still face the same problem and use ionic 5

miffat commented 2 years ago

I call this function in app.component to detect the keyboard show or hide and if show will add scrollIntoView so I don't need to add resizeOnFullScreen in config.xml that will have problem with statusbar plugin coz I need to use statusbar overlay function

subscribeKeyboard() {

this.keyboardSubscribeHide = this.keyboard.onKeyboardDidHide().subscribe((res: any) => {
  console.log('onKeyboardDidHide', res)
  this.zone.run(() => {
    this.keyboard_visibility = false;
  })
})

this.keyboardSubscribeShow = this.keyboard.onKeyboardDidShow().subscribe((res: any) => {
  console.log('onKeyboardDidShow', res)
  this.zone.run(() => {
    this.keyboard_visibility = true;

    if (!this.ios) {
      document.activeElement.scrollIntoView(true);
    }
  })
})

}

this can do the trick and the keyboard_visibility variable here to show or hide my ion-footer if keyboard show/hide

Note: this.ios is the indicator if the platform is ios or not, if not ios only add document.activeElement.scrollIntoView(true);