Skip to content

Commit fa3f67e

Browse files
zach-klippensteinGerrit Code Review
authored andcommitted
Merge changes I6a6d7518,I2740d94a into androidx-main
* changes: Use SoftwareKeyboardControllerCompat in Compose Add SoftwareKeyboardControllerCompat
2 parents b3c9b28 + 324e5fe commit fa3f67e

File tree

11 files changed

+607
-239
lines changed

11 files changed

+607
-239
lines changed

compose/foundation/foundation/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ dependencies {
4343
implementation(libs.kotlinStdlibCommon)
4444
implementation(project(":compose:foundation:foundation-layout"))
4545
implementation(project(':emoji2:emoji2'))
46-
implementation("androidx.core:core:1.9.0")
46+
implementation(project(':core:core'))
4747
implementation("androidx.compose.ui:ui-graphics:1.2.1")
4848
implementation("androidx.compose.ui:ui-text:1.2.1")
4949
implementation("androidx.compose.ui:ui-util:1.2.1")
@@ -111,7 +111,7 @@ if(AndroidXComposePlugin.isMultiplatformEnabled(project)) {
111111
androidMain.dependencies {
112112
api("androidx.annotation:annotation:1.1.0")
113113
implementation(project(':emoji2:emoji2'))
114-
implementation("androidx.core:core:1.9.0")
114+
implementation(project(":core:core"))
115115
}
116116

117117
desktopMain.dependencies {

compose/foundation/foundation/src/androidMain/kotlin/androidx/compose/foundation/text2/input/internal/ComposeInputMethodManager.kt

Lines changed: 6 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,16 @@
1616

1717
package androidx.compose.foundation.text2.input.internal
1818

19-
import android.app.Activity
2019
import android.content.Context
21-
import android.content.ContextWrapper
2220
import android.os.Build
2321
import android.view.KeyEvent
2422
import android.view.View
25-
import android.view.Window
2623
import android.view.inputmethod.BaseInputConnection
2724
import android.view.inputmethod.ExtractedText
2825
import android.view.inputmethod.InputMethodManager
29-
import androidx.annotation.DoNotInline
3026
import androidx.annotation.RequiresApi
3127
import androidx.annotation.RestrictTo
32-
import androidx.compose.ui.window.DialogWindowProvider
33-
import androidx.core.view.WindowInsetsCompat
34-
import androidx.core.view.WindowInsetsControllerCompat
28+
import androidx.core.view.SoftwareKeyboardControllerCompat
3529
import org.jetbrains.annotations.TestOnly
3630

3731
/**
@@ -81,7 +75,6 @@ internal fun ComposeInputMethodManager(view: View): ComposeInputMethodManager =
8175
/** This lets us swap out the implementation in our own tests. */
8276
private var ComposeInputMethodManagerFactory: (View) -> ComposeInputMethodManager = { view ->
8377
when {
84-
Build.VERSION.SDK_INT >= 30 -> ComposeInputMethodManagerImplApi30(view)
8578
Build.VERSION.SDK_INT >= 24 -> ComposeInputMethodManagerImplApi24(view)
8679
else -> ComposeInputMethodManagerImplApi21(view)
8780
}
@@ -109,18 +102,19 @@ private abstract class ComposeInputMethodManagerImpl(protected val view: View) :
109102

110103
private var imm: InputMethodManager? = null
111104

105+
private val softwareKeyboardControllerCompat =
106+
SoftwareKeyboardControllerCompat(view)
107+
112108
override fun restartInput() {
113109
requireImm().restartInput(view)
114110
}
115111

116112
override fun showSoftInput() {
117-
view.post {
118-
requireImm().showSoftInput(view, 0)
119-
}
113+
softwareKeyboardControllerCompat.show()
120114
}
121115

122116
override fun hideSoftInput() {
123-
requireImm().hideSoftInputFromWindow(view.windowToken, 0)
117+
softwareKeyboardControllerCompat.hide()
124118
}
125119

126120
override fun updateExtractedText(
@@ -175,43 +169,3 @@ private open class ComposeInputMethodManagerImplApi24(view: View) :
175169
requireImm().dispatchKeyEventFromInputMethod(view, event)
176170
}
177171
}
178-
179-
@RequiresApi(30)
180-
private class ComposeInputMethodManagerImplApi30(view: View) :
181-
ComposeInputMethodManagerImplApi24(view) {
182-
183-
/**
184-
* Get a [WindowInsetsControllerCompat] for the view. This returns a new instance every time,
185-
* since the view may return null or not null at different times depending on window attach
186-
* state.
187-
*/
188-
private val insetsControllerCompat
189-
// This can return null when, for example, the view is not attached to a window.
190-
get() = view.findWindow()?.let { WindowInsetsControllerCompat(it, view) }
191-
192-
@DoNotInline
193-
override fun showSoftInput() {
194-
insetsControllerCompat
195-
?.apply { show(WindowInsetsCompat.Type.ime()) }
196-
?: super.showSoftInput()
197-
}
198-
199-
@DoNotInline
200-
override fun hideSoftInput() {
201-
insetsControllerCompat
202-
?.apply { hide(WindowInsetsCompat.Type.ime()) }
203-
?: super.showSoftInput()
204-
}
205-
206-
// TODO(b/221889664) Replace with composition local when available.
207-
private fun View.findWindow(): Window? =
208-
(parent as? DialogWindowProvider)?.window
209-
?: context.findWindow()
210-
211-
private tailrec fun Context.findWindow(): Window? =
212-
when (this) {
213-
is Activity -> window
214-
is ContextWrapper -> baseContext.findWindow()
215-
else -> null
216-
}
217-
}

compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/text/input/InputMethodManager.kt

Lines changed: 5 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -16,19 +16,11 @@
1616

1717
package androidx.compose.ui.text.input
1818

19-
import android.app.Activity
2019
import android.content.Context
21-
import android.content.ContextWrapper
22-
import android.os.Build
2320
import android.util.Log
2421
import android.view.View
25-
import android.view.Window
2622
import android.view.inputmethod.ExtractedText
27-
import androidx.annotation.DoNotInline
28-
import androidx.annotation.RequiresApi
29-
import androidx.compose.ui.window.DialogWindowProvider
30-
import androidx.core.view.WindowInsetsCompat
31-
import androidx.core.view.WindowInsetsControllerCompat
23+
import androidx.core.view.SoftwareKeyboardControllerCompat
3224

3325
internal interface InputMethodManager {
3426
fun restartInput()
@@ -61,11 +53,8 @@ internal class InputMethodManagerImpl(private val view: View) : InputMethodManag
6153
as android.view.inputmethod.InputMethodManager
6254
}
6355

64-
private val helper = if (Build.VERSION.SDK_INT < 30) {
65-
ImmHelper21(view)
66-
} else {
67-
ImmHelper30(view)
68-
}
56+
private val softwareKeyboardControllerCompat =
57+
SoftwareKeyboardControllerCompat(view)
6958

7059
override fun restartInput() {
7160
imm.restartInput(view)
@@ -76,11 +65,11 @@ internal class InputMethodManagerImpl(private val view: View) : InputMethodManag
7665
Log.d(TAG, "InputMethodManagerImpl: requesting soft input on non focused field")
7766
}
7867

79-
helper.showSoftInput(imm)
68+
softwareKeyboardControllerCompat.show()
8069
}
8170

8271
override fun hideSoftInput() {
83-
helper.hideSoftInput(imm)
72+
softwareKeyboardControllerCompat.hide()
8473
}
8574

8675
override fun updateExtractedText(
@@ -99,90 +88,3 @@ internal class InputMethodManagerImpl(private val view: View) : InputMethodManag
9988
imm.updateSelection(view, selectionStart, selectionEnd, compositionStart, compositionEnd)
10089
}
10190
}
102-
103-
private interface ImmHelper {
104-
fun showSoftInput(imm: android.view.inputmethod.InputMethodManager)
105-
fun hideSoftInput(imm: android.view.inputmethod.InputMethodManager)
106-
}
107-
108-
private class ImmHelper21(private val view: View) : ImmHelper {
109-
110-
@DoNotInline
111-
override fun showSoftInput(imm: android.view.inputmethod.InputMethodManager) {
112-
view.post {
113-
imm.showSoftInput(view, 0)
114-
}
115-
}
116-
117-
@DoNotInline
118-
override fun hideSoftInput(imm: android.view.inputmethod.InputMethodManager) {
119-
imm.hideSoftInputFromWindow(view.windowToken, 0)
120-
}
121-
}
122-
123-
@RequiresApi(30)
124-
private class ImmHelper30(private val view: View) : ImmHelper {
125-
126-
/**
127-
* Get a [WindowInsetsControllerCompat] for the view. This returns a new instance every time,
128-
* since the view may return null or not null at different times depending on window attach
129-
* state.
130-
*/
131-
private val insetsControllerCompat
132-
// This can return null when, for example, the view is not attached to a window.
133-
get() = view.findWindow()?.let { WindowInsetsControllerCompat(it, view) }
134-
135-
/**
136-
* This class falls back to the legacy implementation when the window insets controller isn't
137-
* available.
138-
*/
139-
private val immHelper21: ImmHelper21
140-
get() = _immHelper21 ?: ImmHelper21(view).also { _immHelper21 = it }
141-
private var _immHelper21: ImmHelper21? = null
142-
143-
@DoNotInline
144-
override fun showSoftInput(imm: android.view.inputmethod.InputMethodManager) {
145-
insetsControllerCompat?.apply {
146-
show(WindowInsetsCompat.Type.ime())
147-
} ?: immHelper21.showSoftInput(imm)
148-
}
149-
150-
@DoNotInline
151-
override fun hideSoftInput(imm: android.view.inputmethod.InputMethodManager) {
152-
insetsControllerCompat?.apply {
153-
hide(WindowInsetsCompat.Type.ime())
154-
} ?: immHelper21.hideSoftInput(imm)
155-
}
156-
157-
// TODO(b/221889664) Replace with composition local when available.
158-
private fun View.findWindow(): Window? {
159-
var view: View = this
160-
while (true) {
161-
if (view is DialogWindowProvider) {
162-
return view.window
163-
}
164-
val parent = view.parent as? View
165-
if (parent == null) {
166-
// Found the decor view of the current window. We can't get the window directly from
167-
// the view, but we can get the window of the view's context. However, there's no
168-
// guarantee that the view and its context share a window. For example, in a dialog,
169-
// the view's context is the activity hosting the dialog, so the context's view tree
170-
// will be entirely separate from the dialog's view tree. We only return the
171-
// context's window if we are actually in that window, i.e. have the same root view.
172-
// Otherwise we return null to fallback to not using window insets.
173-
val windowFromContext = view.context.findWindow() ?: return null
174-
val windowDecorView = windowFromContext.decorView
175-
return if (windowDecorView === view) windowFromContext else null
176-
}
177-
view = parent
178-
}
179-
}
180-
181-
private tailrec fun Context.findWindow(): Window? {
182-
return when (this) {
183-
is Activity -> window
184-
is ContextWrapper -> baseContext.findWindow()
185-
else -> null
186-
}
187-
}
188-
}

core/core/api/current.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2716,6 +2716,12 @@ package androidx.core.view {
27162716
method public int computeVerticalScrollRange();
27172717
}
27182718

2719+
public final class SoftwareKeyboardControllerCompat {
2720+
ctor public SoftwareKeyboardControllerCompat(android.view.View);
2721+
method public void hide();
2722+
method public void show();
2723+
}
2724+
27192725
public interface TintableBackgroundView {
27202726
method public android.content.res.ColorStateList? getSupportBackgroundTintList();
27212727
method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();

core/core/api/public_plus_experimental_current.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2723,6 +2723,12 @@ package androidx.core.view {
27232723
method public int computeVerticalScrollRange();
27242724
}
27252725

2726+
public final class SoftwareKeyboardControllerCompat {
2727+
ctor public SoftwareKeyboardControllerCompat(android.view.View);
2728+
method public void hide();
2729+
method public void show();
2730+
}
2731+
27262732
public interface TintableBackgroundView {
27272733
method public android.content.res.ColorStateList? getSupportBackgroundTintList();
27282734
method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();

core/core/api/restricted_current.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3161,6 +3161,12 @@ package androidx.core.view {
31613161
method public int computeVerticalScrollRange();
31623162
}
31633163

3164+
public final class SoftwareKeyboardControllerCompat {
3165+
ctor public SoftwareKeyboardControllerCompat(android.view.View);
3166+
method public void hide();
3167+
method public void show();
3168+
}
3169+
31643170
public interface TintableBackgroundView {
31653171
method public android.content.res.ColorStateList? getSupportBackgroundTintList();
31663172
method public android.graphics.PorterDuff.Mode? getSupportBackgroundTintMode();

core/core/src/androidTest/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,12 @@
122122
android:exported="true"
123123
android:theme="@android:style/Theme.Light.NoTitleBar" />
124124

125+
<activity
126+
android:name="androidx.core.view.SoftwareKeyboardControllerCompatActivity"
127+
android:exported="true"
128+
android:windowSoftInputMode="adjustResize"
129+
android:theme="@android:style/Theme.Light.NoTitleBar" />
130+
125131
<activity
126132
android:name="androidx.core.app.GetSystemLocalesActivity"
127133
android:exported="true" />
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright 2023 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.core.view;
18+
19+
import android.support.v4.BaseTestActivity;
20+
21+
import androidx.core.test.R;
22+
23+
public class SoftwareKeyboardControllerCompatActivity extends BaseTestActivity {
24+
@Override
25+
protected int getContentViewLayoutResId() {
26+
return R.layout.insets_compat_activity;
27+
}
28+
}

0 commit comments

Comments
 (0)