Compare commits
4 Commits
e17903f40f
...
e004862b00
| Author | SHA1 | Date | |
|---|---|---|---|
| e004862b00 | |||
| ff3cab9531 | |||
| 79f229de6d | |||
| 73c3683740 |
@@ -34,8 +34,8 @@ android {
|
||||
applicationId = "de.mm20.launcher2"
|
||||
minSdk = libs.versions.minSdk.get().toInt()
|
||||
targetSdk = libs.versions.targetSdk.get().toInt()
|
||||
versionCode = System.getenv("VERSION_CODE_OVERRIDE")?.toIntOrNull() ?: 2026091001
|
||||
versionName = "1.40.2-typing.2"
|
||||
versionCode = System.getenv("VERSION_CODE_OVERRIDE")?.toIntOrNull() ?: 2026091002
|
||||
versionName = "1.40.2-typing.3"
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -143,9 +144,15 @@ fun SearchBar(
|
||||
val textFieldValue = remember {
|
||||
mutableStateOf(TextFieldValue(value, TextRange(value.length)))
|
||||
}
|
||||
// Characters the user typed are already in this value; onValueChange below keeps the cursor
|
||||
// where the user put it. Text that changed from the outside - the search was reset, or a
|
||||
// character was inserted by the physical keyboard handler - is adopted here, with the cursor at
|
||||
// the end. This must not write state during composition, so it runs in a side effect after it.
|
||||
SideEffect {
|
||||
if (textFieldValue.value.text != value) {
|
||||
textFieldValue.value = TextFieldValue(value, TextRange(value.length))
|
||||
}
|
||||
}
|
||||
|
||||
LauncherCard(
|
||||
modifier = modifier
|
||||
|
||||
@@ -63,6 +63,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.listSaver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
@@ -79,6 +80,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||
@@ -988,6 +990,7 @@ internal fun LauncherScaffold(
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val activity = LocalActivity.current as AppCompatActivity
|
||||
val view = LocalView.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
val wallpaperManager = remember(activity) { WallpaperManager.getInstance(activity) }
|
||||
|
||||
@@ -1136,10 +1139,27 @@ internal fun LauncherScaffold(
|
||||
// The search does not survive leaving the launcher, not even for a short app
|
||||
// switch: coming back should always show a fresh search.
|
||||
if (!state.isLocked) {
|
||||
// Clear the query first: the page transition below animates and must not
|
||||
// be what keeps a stale query around.
|
||||
searchVM.reset()
|
||||
if (state.currentComponent?.survivesPause != true) {
|
||||
state.reset()
|
||||
}
|
||||
searchVM.reset()
|
||||
}
|
||||
}
|
||||
// The framework hands the keyboard focus - and with it the on-screen keyboard -
|
||||
// back to the search bar's text field whenever the launcher's window regains
|
||||
// focus, even though no search is open. The home screen would then sit behind an
|
||||
// on-screen keyboard and the physical keyboard would type into the search bar
|
||||
// without the search results ever opening, because the characters are delivered
|
||||
// to the focused text field instead of the key event handler. Give that focus up
|
||||
// again, one frame after the resume, so that it also works when the framework
|
||||
// grants the focus while the window is coming back.
|
||||
if (activity.pauseTime > 0L) {
|
||||
withFrameNanos { }
|
||||
if (state.currentComponent !is SearchComponent) {
|
||||
focusManager.clearFocus()
|
||||
activity.hideSoftKeyboard()
|
||||
}
|
||||
}
|
||||
awaitCancellation()
|
||||
@@ -1409,8 +1429,11 @@ internal fun LauncherScaffold(
|
||||
// as usual from now on.
|
||||
state.isTypingSearch = false
|
||||
scope.launch { state.onSearchBarTap() }
|
||||
state.isSearchBarFocused = true
|
||||
}
|
||||
// Keep this in sync with the real focus state. It must go back to false when
|
||||
// the text field loses focus, otherwise the launcher keeps asking for it
|
||||
// (LauncherSearchBar requests focus whenever this is true).
|
||||
state.isSearchBarFocused = it
|
||||
},
|
||||
onKeyboardActionGo = if (launchOnEnter) {
|
||||
{ searchVM.launchBestMatchOrAction(activity) }
|
||||
|
||||
@@ -4,9 +4,11 @@ import android.app.Activity
|
||||
import android.app.ActivityOptions
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProviderInfo
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
@@ -809,6 +811,7 @@ fun ColumnScope.ConfigureAppWidget(
|
||||
bottom = 8.dp,
|
||||
),
|
||||
onClick = {
|
||||
try {
|
||||
appWidgetHost.startAppWidgetConfigureActivityForResult(
|
||||
lifecycleOwner as Activity,
|
||||
widget.config.widgetId,
|
||||
@@ -824,6 +827,16 @@ fun ColumnScope.ConfigureAppWidget(
|
||||
.toBundle()
|
||||
}
|
||||
)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// A widget can advertise WIDGET_FEATURE_RECONFIGURABLE without providing a
|
||||
// configure activity (or the app providing it was uninstalled). Starting it
|
||||
// then throws, which would crash the launcher.
|
||||
Log.w("MM20", "No configure activity for widget ${widget.config.widgetId}", e)
|
||||
} catch (e: SecurityException) {
|
||||
// The configure activity refused to be started, e.g. because the app owning
|
||||
// the widget is not allowed to start activities from the background.
|
||||
Log.w("MM20", "Widget configure activity refused to start", e)
|
||||
}
|
||||
}) {
|
||||
Text(
|
||||
stringResource(id = R.string.widget_config_appwidget_configure)
|
||||
|
||||
Reference in New Issue
Block a user