Fix inset setting code

This commit is contained in:
Aleksandras Kostarevas 2023-08-14 16:53:33 +03:00
parent 948ba3c6f0
commit d81d79ea67
2 changed files with 55 additions and 90 deletions

View File

@ -3,55 +3,26 @@ package org.futo.inputmethod.latin
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.inputmethodservice.InputMethodService import android.inputmethodservice.InputMethodService
import android.os.Build
import android.text.InputType
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View import android.view.View
import android.view.inputmethod.CompletionInfo import android.view.inputmethod.CompletionInfo
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.view.inputmethod.InputMethodSubtype import android.view.inputmethod.InputMethodSubtype
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.scale
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.ViewModelStore import androidx.lifecycle.ViewModelStore
import androidx.lifecycle.ViewModelStoreOwner import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.findViewTreeLifecycleOwner import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.lifecycle.setViewTreeLifecycleOwner import androidx.lifecycle.setViewTreeLifecycleOwner
@ -124,6 +95,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
} }
private var legacyInputView: View? = null private var legacyInputView: View? = null
private var touchableHeight: Int = 0
override fun onCreateInputView(): View { override fun onCreateInputView(): View {
legacyInputView = latinIMELegacy.onCreateInputView() legacyInputView = latinIMELegacy.onCreateInputView()
composeView = ComposeView(this).apply { composeView = ComposeView(this).apply {
@ -134,14 +106,16 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
} }
composeView?.setContent { composeView?.setContent {
Surface(color = Color.Blue) { Column {
Column { Spacer(modifier = Modifier.weight(1.0f))
Text("Example Compose Element", color = Color.Red) Surface(modifier = Modifier.onSizeChanged {
Text("The keyboard below is wrapped in AndroidView", color = Color.Red) touchableHeight = it.height
}, color = MaterialTheme.colorScheme.surface) {
AndroidView(factory = { Column {
legacyInputView!! AndroidView(factory = {
}, update = { }) legacyInputView!!
}, update = { })
}
} }
} }
} }
@ -151,6 +125,7 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
override fun setInputView(view: View?) { override fun setInputView(view: View?) {
super.setInputView(view) super.setInputView(view)
latinIMELegacy.setComposeInputView(composeView!!)
latinIMELegacy.setInputView(legacyInputView) latinIMELegacy.setInputView(legacyInputView)
} }
@ -225,15 +200,37 @@ class LatinIME : InputMethodService(), LifecycleOwner, ViewModelStoreOwner, Save
latinIMELegacy.onDisplayCompletions(completions) latinIMELegacy.onDisplayCompletions(completions)
} }
/*
// TODO: This seems to not factor in the dimensions of the Compose elements
// and bottom parts of the keyboard start to pass input through to the app.
// The keyboard seems to work fine without this method. What was it for?
override fun onComputeInsets(outInsets: Insets?) { override fun onComputeInsets(outInsets: Insets?) {
super.onComputeInsets(outInsets) // This method may be called before {@link #setInputView(View)}.
latinIMELegacy.onComputeInsets(outInsets) if (legacyInputView == null) {
return
}
val inputHeight: Int = composeView!!.height
if (latinIMELegacy.isImeSuppressedByHardwareKeyboard && !legacyInputView!!.isShown) {
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
// no visual element will be shown on the screen.
latinIMELegacy.setInsets(outInsets!!.apply {
contentTopInsets = inputHeight
visibleTopInsets = inputHeight
})
return
}
val visibleTopY = inputHeight - touchableHeight
val touchLeft = 0
val touchTop = visibleTopY
val touchRight = legacyInputView!!.width
val touchBottom = inputHeight
latinIMELegacy.setInsets(outInsets!!.apply {
touchableInsets = Insets.TOUCHABLE_INSETS_REGION;
touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom);
contentTopInsets = visibleTopY
visibleTopInsets = visibleTopY
})
} }
*/
override fun onShowInputRequested(flags: Int, configChange: Boolean): Boolean { override fun onShowInputRequested(flags: Int, configChange: Boolean): Boolean {
return latinIMELegacy.onShowInputRequested(flags, configChange) || super.onShowInputRequested(flags, configChange) return latinIMELegacy.onShowInputRequested(flags, configChange) || super.onShowInputRequested(flags, configChange)

View File

@ -158,6 +158,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
// TODO: Move these {@link View}s to {@link KeyboardSwitcher}. // TODO: Move these {@link View}s to {@link KeyboardSwitcher}.
private View mInputView; private View mInputView;
private View mComposeInputView;
private InsetsUpdater mInsetsUpdater; private InsetsUpdater mInsetsUpdater;
private SuggestionStripView mSuggestionStripView; private SuggestionStripView mSuggestionStripView;
@ -762,7 +763,7 @@ public class LatinIMELegacy implements KeyboardActionListener,
mInputLogic.recycle(); mInputLogic.recycle();
} }
private boolean isImeSuppressedByHardwareKeyboard() { public boolean isImeSuppressedByHardwareKeyboard() {
final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance(); final KeyboardSwitcher switcher = KeyboardSwitcher.getInstance();
return !onEvaluateInputViewShown() && switcher.isImeSuppressedByHardwareKeyboard( return !onEvaluateInputViewShown() && switcher.isImeSuppressedByHardwareKeyboard(
mSettings.getCurrent(), switcher.getKeyboardSwitchState()); mSettings.getCurrent(), switcher.getKeyboardSwitchState());
@ -836,10 +837,15 @@ public class LatinIMELegacy implements KeyboardActionListener,
mIsHardwareAcceleratedDrawingEnabled); mIsHardwareAcceleratedDrawingEnabled);
} }
public void setInputView(final View view) {
mInputView = view; public void setComposeInputView(final View view) {
mComposeInputView = view;
mInsetsUpdater = ViewOutlineProviderCompatUtils.setInsetsOutlineProvider(view); mInsetsUpdater = ViewOutlineProviderCompatUtils.setInsetsOutlineProvider(view);
updateSoftInputWindowLayoutParameters(); updateSoftInputWindowLayoutParameters();
}
public void setInputView(final View view) {
mInputView = view;
mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
if (hasSuggestionStripView()) { if (hasSuggestionStripView()) {
mSuggestionStripView.setListener(this, view); mSuggestionStripView.setListener(this, view);
@ -1196,42 +1202,8 @@ public class LatinIMELegacy implements KeyboardActionListener,
setSuggestedWords(suggestedWords); setSuggestedWords(suggestedWords);
} }
public void onComputeInsets(final InputMethodService.Insets outInsets) { public void setInsets(final InputMethodService.Insets insets) {
// This method may be called before {@link #setInputView(View)}. mInsetsUpdater.setInsets(insets);
if (mInputView == null) {
return;
}
final SettingsValues settingsValues = mSettings.getCurrent();
final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
if (visibleKeyboardView == null || !hasSuggestionStripView()) {
return;
}
final int inputHeight = mInputView.getHeight();
if (isImeSuppressedByHardwareKeyboard() && !visibleKeyboardView.isShown()) {
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
// no visual element will be shown on the screen.
outInsets.contentTopInsets = inputHeight;
outInsets.visibleTopInsets = inputHeight;
mInsetsUpdater.setInsets(outInsets);
return;
}
final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes()
&& mSuggestionStripView.getVisibility() == View.VISIBLE)
? mSuggestionStripView.getHeight() : 0;
final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight;
mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY);
// Need to set expanded touchable region only if a keyboard view is being shown.
if (visibleKeyboardView.isShown()) {
final int touchLeft = 0;
final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
final int touchRight = visibleKeyboardView.getWidth();
final int touchBottom = inputHeight;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom);
}
outInsets.contentTopInsets = visibleTopY;
outInsets.visibleTopInsets = visibleTopY;
mInsetsUpdater.setInsets(outInsets);
} }
public void startShowingInputView(final boolean needsToLoadKeyboard) { public void startShowingInputView(final boolean needsToLoadKeyboard) {
@ -1287,16 +1259,13 @@ public class LatinIMELegacy implements KeyboardActionListener,
} }
private void updateSoftInputWindowLayoutParameters() { private void updateSoftInputWindowLayoutParameters() {
// TODO: This seems to mess with the compose UI a lot, and the keyboard
// works fine without it. What was it for?
/*
// Override layout parameters to expand {@link SoftInputWindow} to the entire screen. // Override layout parameters to expand {@link SoftInputWindow} to the entire screen.
// See {@link InputMethodService#setinputView(View)} and // See {@link InputMethodService#setinputView(View)} and
// {@link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}. // {@link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}.
final Window window = mInputMethodService.getWindow().getWindow(); final Window window = mInputMethodService.getWindow().getWindow();
ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT); ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT);
// This method may be called before {@link #setInputView(View)}. // This method may be called before {@link #setInputView(View)}.
if (mInputView != null) { if (mComposeInputView != null) {
// In non-fullscreen mode, {@link InputView} and its parent inputArea should expand to // In non-fullscreen mode, {@link InputView} and its parent inputArea should expand to
// the entire screen and be placed at the bottom of {@link SoftInputWindow}. // the entire screen and be placed at the bottom of {@link SoftInputWindow}.
// In fullscreen mode, these shouldn't expand to the entire screen and should be // In fullscreen mode, these shouldn't expand to the entire screen and should be
@ -1308,9 +1277,8 @@ public class LatinIMELegacy implements KeyboardActionListener,
final View inputArea = window.findViewById(android.R.id.inputArea); final View inputArea = window.findViewById(android.R.id.inputArea);
ViewLayoutUtils.updateLayoutHeightOf(inputArea, layoutHeight); ViewLayoutUtils.updateLayoutHeightOf(inputArea, layoutHeight);
ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM); ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM);
ViewLayoutUtils.updateLayoutHeightOf(mInputView, layoutHeight); ViewLayoutUtils.updateLayoutHeightOf(mComposeInputView, layoutHeight);
} }
*/
} }
int getCurrentAutoCapsState() { int getCurrentAutoCapsState() {