diff --git a/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/KP2AKeyboard.java b/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/KP2AKeyboard.java index 128391312..f1a40f6bb 100644 --- a/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/KP2AKeyboard.java +++ b/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/KP2AKeyboard.java @@ -34,6 +34,7 @@ import android.media.AudioManager; import android.net.Uri; import android.os.Build; +import android.view.WindowInsets; import android.os.Debug; import android.os.Handler; import android.os.Message; @@ -51,6 +52,7 @@ import android.view.HapticFeedbackConstants; import android.view.KeyEvent; import android.view.View; +import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; @@ -543,6 +545,11 @@ public void onConfigurationChanged(Configuration conf) { mConfigurationChanging = true; super.onConfigurationChanged(conf); mConfigurationChanging = false; + // Re-apply nav bar padding after orientation change; post it so the + // new window layout has settled before we read the insets. + mHandler.post(new Runnable() { + @Override public void run() { scheduleApplyNavBarInsetsToInputViewLayout(); } + }); } @Override @@ -628,6 +635,7 @@ public void onStartInputView(EditorInfo attribute, boolean restarting) { checkReCorrectionOnStart(); tryKp2aAutoFill(attribute); + scheduleApplyNavBarInsetsToInputViewLayout(); if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } @@ -1036,6 +1044,62 @@ public void setCandidatesViewShown(boolean shown) { setCandidatesViewShownInternal(shown, true /* needsInputViewShown */); } + @Override + public void onWindowShown() { + super.onWindowShown(); + scheduleApplyNavBarInsetsToInputViewLayout(); + } + + private void scheduleApplyNavBarInsetsToInputViewLayout() { + applyNavBarInsetsToInputViewLayout(); + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + applyNavBarInsetsToInputViewLayout(); + } + }, 100); + } + + private void applyNavBarInsetsToInputViewLayout() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return; + final View inputView = mKeyboardSwitcher.getInputView(); + if (inputView == null) return; + + WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE); + android.view.WindowMetrics metrics = wm.getCurrentWindowMetrics(); + android.graphics.Insets nav = + metrics.getWindowInsets().getInsets(WindowInsets.Type.navigationBars()); + + android.view.Window win = getWindow() != null ? getWindow().getWindow() : null; + View decor = win != null ? win.getDecorView() : null; + int decorWidth = decor != null ? decor.getWidth() : 0; + int decorHeight = decor != null ? decor.getHeight() : 0; + int displayWidth = metrics.getBounds().width(); + int displayHeight = metrics.getBounds().height(); + + final int tolerancePx = 2; + boolean windowAlreadyExcludesHorizontalNav = decorWidth > 0 + && decorWidth <= (displayWidth - nav.left - nav.right + tolerancePx); + + int effectiveLeft = windowAlreadyExcludesHorizontalNav ? 0 : nav.left; + int effectiveRight = windowAlreadyExcludesHorizontalNav ? 0 : nav.right; + // Do not infer bottom-nav exclusion from decor height: IME windows are naturally + // shorter than display height, so that heuristic wrongly drops bottom inset. + int effectiveBottom = nav.bottom; + + ViewGroup.LayoutParams lp = inputView.getLayoutParams(); + if (lp instanceof ViewGroup.MarginLayoutParams) { + ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp; + if (mlp.leftMargin != effectiveLeft || mlp.rightMargin != effectiveRight + || mlp.bottomMargin != effectiveBottom) { + mlp.leftMargin = effectiveLeft; + mlp.rightMargin = effectiveRight; + mlp.bottomMargin = effectiveBottom; + inputView.setLayoutParams(mlp); + } + } + } + @Override public void onComputeInsets(InputMethodService.Insets outInsets) { super.onComputeInsets(outInsets); diff --git a/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/KeyboardSwitcher.java b/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/KeyboardSwitcher.java index e6847814a..7421f4a47 100644 --- a/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/KeyboardSwitcher.java +++ b/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/KeyboardSwitcher.java @@ -19,12 +19,9 @@ import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; -import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; import android.view.InflateException; -import android.view.View; -import android.view.WindowInsets; import java.lang.ref.SoftReference; import java.util.Arrays; @@ -532,27 +529,6 @@ private void changeLatinKeyboardView(int newLayout, boolean forceReset) { } } mInputView.setOnKeyboardActionListener(mInputMethodService); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mInputView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { - @Override - public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { - // Untere Systembar-Höhe holen - int insetBottom = insets.getSystemWindowInsetBottom(); - - // Padding nur unten anpassen - v.setPadding( - v.getPaddingLeft(), - v.getPaddingTop(), - v.getPaddingRight(), - insetBottom - ); - - // Insets normal weiterreichen - return insets; - } - }); - } mLayoutId = newLayout; } diff --git a/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/LatinKeyboard.java b/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/LatinKeyboard.java index 45bc65d90..34a9938bb 100644 --- a/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/LatinKeyboard.java +++ b/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/LatinKeyboard.java @@ -90,6 +90,13 @@ public class LatinKeyboard extends Keyboard { private int mPrefLetterY; private int mPrefDistance; + private int[] mOriginalKeyX; + private int[] mOriginalKeyWidth; + private int[] mOriginalKeyGap; + private int mOriginalMinWidth = -1; + private int mDynamicMinWidth = -1; + private int[] mAllKeyIndices; + // TODO: generalize for any keyboardId private boolean mIsBlackSym; @@ -786,10 +793,52 @@ public int[] getNearestKeys(int x, int y) { if (mCurrentlyInSpace) { return mSpaceKeyIndexArray; } else { - // Avoid dead pixels at edges of the keyboard - return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)), - Math.max(0, Math.min(y, getHeight() - 1))); + // Avoid relying on Keyboard's internal nearest-key grid after dynamic resize, + // as those caches are private and not rebuildable from here. + if (mAllKeyIndices == null || mAllKeyIndices.length != getKeys().size()) { + mAllKeyIndices = new int[getKeys().size()]; + for (int i = 0; i < mAllKeyIndices.length; i++) { + mAllKeyIndices[i] = i; + } + } + return mAllKeyIndices; + } + } + + @Override + public int getMinWidth() { + return mDynamicMinWidth > 0 ? mDynamicMinWidth : super.getMinWidth(); + } + + public void resizeToWidth(int newWidth) { + if (newWidth <= 0) return; + final List keys = getKeys(); + if (keys == null || keys.isEmpty()) return; + + if (mOriginalKeyX == null || mOriginalKeyX.length != keys.size()) { + mOriginalKeyX = new int[keys.size()]; + mOriginalKeyWidth = new int[keys.size()]; + mOriginalKeyGap = new int[keys.size()]; + mOriginalMinWidth = super.getMinWidth(); + for (int i = 0; i < keys.size(); i++) { + Key key = keys.get(i); + mOriginalKeyX[i] = key.x; + mOriginalKeyWidth[i] = key.width; + mOriginalKeyGap[i] = key.gap; + } + } + + final int baseWidth = Math.max(1, mOriginalMinWidth); + final float scale = ((float) newWidth) / baseWidth; + int maxRight = 0; + for (int i = 0; i < keys.size(); i++) { + Key key = keys.get(i); + key.x = Math.max(0, Math.round(mOriginalKeyX[i] * scale)); + key.width = Math.max(1, Math.round(mOriginalKeyWidth[i] * scale)); + key.gap = Math.max(0, Math.round(mOriginalKeyGap[i] * scale)); + maxRight = Math.max(maxRight, key.x + key.width); } + mDynamicMinWidth = Math.max(1, maxRight); } private int indexOf(int code) { diff --git a/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/LatinKeyboardBaseView.java b/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/LatinKeyboardBaseView.java index dc75569b8..4240e5a6d 100644 --- a/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/LatinKeyboardBaseView.java +++ b/src/java/KP2ASoftkeyboard_AS/app/src/main/java/keepass2android/softkeyboard/LatinKeyboardBaseView.java @@ -748,6 +748,18 @@ private void computeProximityThreshold(Keyboard keyboard) { @Override public void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); + if (mKeyboard instanceof LatinKeyboard) { + final int newKeyboardWidth = Math.max(1, w - getPaddingLeft() - getPaddingRight()); + ((LatinKeyboard)mKeyboard).resizeToWidth(newKeyboardWidth); + mKeys = mKeyDetector.setKeyboard(mKeyboard, -getPaddingLeft(), + -getPaddingTop() + mVerticalCorrection); + for (PointerTracker tracker : mPointerTrackers) { + tracker.setKeyboard(mKeys, mKeyHysteresisDistance); + } + computeProximityThreshold(mKeyboard); + mKeyboardChanged = true; + invalidateAllKeys(); + } // Release the buffer, if any and it will be reallocated on the next draw mBuffer = null; } diff --git a/src/keepass2android-app/EntryEditActivity.cs b/src/keepass2android-app/EntryEditActivity.cs index 7beb3a5a7..1c97a48f9 100644 --- a/src/keepass2android-app/EntryEditActivity.cs +++ b/src/keepass2android-app/EntryEditActivity.cs @@ -1540,7 +1540,7 @@ void UpdateExpires() { PopulateText(Resource.Id.entry_expires, GetString(Resource.String.never)); } - ((CheckBox)FindViewById(Resource.Id.entry_expires_checkbox)).Checked = State.Entry.Expires; + ((CheckBox)FindViewById(Resource.Id.entry_expires_checkbox)).Checked = State.Entry.Expires; FindViewById(Resource.Id.entry_expires).Enabled = State.Entry.Expires; }