mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-22 03:58:48 +00:00
Convert ContactFilterView to compose.
This commit is contained in:
@@ -27,9 +27,10 @@ import org.thoughtcrime.securesms.util.ViewUtil;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A search input field for finding recipients.
|
* A search input field for finding recipients.
|
||||||
* <p>
|
*
|
||||||
* In compose, use RecipientSearchField instead.
|
* @deprecated Use the RecipientSearchBar composable instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public final class ContactFilterView extends FrameLayout {
|
public final class ContactFilterView extends FrameLayout {
|
||||||
private OnFilterChangedListener listener;
|
private OnFilterChangedListener listener;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.thoughtcrime.securesms.components.compose
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.view.inputmethod.EditorInfo
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.platform.InterceptPlatformTextInput
|
||||||
|
import androidx.compose.ui.platform.PlatformTextInputMethodRequest
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When [enabled]=true, this function sets the [EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING] flag for all text fields within its content to enable the
|
||||||
|
* incognito keyboard.
|
||||||
|
*
|
||||||
|
* This workaround is needed until it's possible to configure granular IME options for a [TextField].
|
||||||
|
* https://issuetracker.google.com/issues/359257538
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun ProvideIncognitoKeyboard(
|
||||||
|
enabled: Boolean,
|
||||||
|
content: @Composable () -> Unit
|
||||||
|
) {
|
||||||
|
if (enabled) {
|
||||||
|
InterceptPlatformTextInput(
|
||||||
|
interceptor = { request, nextHandler ->
|
||||||
|
val modifiedRequest = PlatformTextInputMethodRequest { outAttributes ->
|
||||||
|
request.createInputConnection(outAttributes).also {
|
||||||
|
if (Build.VERSION.SDK_INT >= 26) {
|
||||||
|
outAttributes.imeOptions = outAttributes.imeOptions or EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nextHandler.startInputMethod(modifiedRequest)
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
content()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,13 +7,14 @@ package org.thoughtcrime.securesms.conversation
|
|||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.StringRes
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.isImeVisible
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -22,12 +23,13 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.platform.LocalConfiguration
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
|
||||||
import androidx.fragment.compose.rememberFragmentState
|
import androidx.fragment.compose.rememberFragmentState
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -40,7 +42,6 @@ import org.signal.core.util.DimensionUnit
|
|||||||
import org.signal.core.util.orNull
|
import org.signal.core.util.orNull
|
||||||
import org.thoughtcrime.securesms.ContactSelectionListFragment
|
import org.thoughtcrime.securesms.ContactSelectionListFragment
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.components.ContactFilterView
|
|
||||||
import org.thoughtcrime.securesms.components.menu.ActionItem
|
import org.thoughtcrime.securesms.components.menu.ActionItem
|
||||||
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
|
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
|
||||||
import org.thoughtcrime.securesms.contacts.ContactSelectionDisplayMode
|
import org.thoughtcrime.securesms.contacts.ContactSelectionDisplayMode
|
||||||
@@ -54,13 +55,14 @@ import org.thoughtcrime.securesms.groups.SelectionLimits
|
|||||||
import org.thoughtcrime.securesms.recipients.PhoneNumber
|
import org.thoughtcrime.securesms.recipients.PhoneNumber
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil
|
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a recipient search and selection UI.
|
* Provides a recipient search and selection UI.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("KotlinConstantConditions")
|
||||||
|
@OptIn(ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun RecipientPicker(
|
fun RecipientPicker(
|
||||||
searchQuery: String,
|
searchQuery: String,
|
||||||
@@ -78,11 +80,26 @@ fun RecipientPicker(
|
|||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
RecipientSearchField(
|
val focusRequester = remember { FocusRequester() }
|
||||||
searchQuery = searchQuery,
|
var shouldRequestFocus by rememberSaveable { mutableStateOf(focusAndShowKeyboard) }
|
||||||
onFilterChanged = { filter -> callbacks.listActions.onSearchQueryChanged(query = filter) },
|
|
||||||
focusAndShowKeyboard = focusAndShowKeyboard,
|
LaunchedEffect(Unit) {
|
||||||
|
if (shouldRequestFocus) {
|
||||||
|
focusRequester.requestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val isImeVisible = WindowInsets.isImeVisible
|
||||||
|
LaunchedEffect(isImeVisible) {
|
||||||
|
shouldRequestFocus = isImeVisible
|
||||||
|
}
|
||||||
|
|
||||||
|
RecipientSearchBar(
|
||||||
|
query = searchQuery,
|
||||||
|
onQueryChange = { filter -> callbacks.listActions.onSearchQueryChanged(query = filter) },
|
||||||
|
onSearch = {},
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.focusRequester(focusRequester)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
)
|
)
|
||||||
@@ -104,54 +121,7 @@ fun RecipientPicker(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Suppress("KotlinConstantConditions")
|
||||||
* A search input field for finding recipients.
|
|
||||||
*
|
|
||||||
* Intended to be a compose-based replacement for [ContactFilterView].
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
private fun RecipientSearchField(
|
|
||||||
searchQuery: String,
|
|
||||||
onFilterChanged: (String) -> Unit,
|
|
||||||
@StringRes hintText: Int? = null,
|
|
||||||
focusAndShowKeyboard: Boolean = false,
|
|
||||||
modifier: Modifier = Modifier
|
|
||||||
) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
val wrappedView = remember {
|
|
||||||
ContactFilterView(context, null, 0).apply {
|
|
||||||
hintText?.let { setHint(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(searchQuery) {
|
|
||||||
wrappedView.setText(searchQuery)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO [jeff] This causes the keyboard to re-open on rotation, which doesn't match the existing behavior of ContactFilterView. To fix this,
|
|
||||||
// RecipientSearchField needs to be converted to compose so we can use FocusRequestor.
|
|
||||||
LaunchedEffect(focusAndShowKeyboard) {
|
|
||||||
if (focusAndShowKeyboard) {
|
|
||||||
wrappedView.focusAndShowKeyboard()
|
|
||||||
} else {
|
|
||||||
wrappedView.clearFocus()
|
|
||||||
ViewUtil.hideKeyboard(wrappedView.context, wrappedView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DisposableEffect(onFilterChanged) {
|
|
||||||
wrappedView.setOnFilterChangedListener { filter -> onFilterChanged(filter) }
|
|
||||||
onDispose {
|
|
||||||
wrappedView.setOnFilterChangedListener(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidView(
|
|
||||||
factory = { wrappedView },
|
|
||||||
modifier = modifier
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun RecipientSearchResultsList(
|
private fun RecipientSearchResultsList(
|
||||||
displayModes: Set<RecipientPicker.DisplayMode>,
|
displayModes: Set<RecipientPicker.DisplayMode>,
|
||||||
@@ -179,6 +149,7 @@ private fun RecipientSearchResultsList(
|
|||||||
val fragmentState = rememberFragmentState()
|
val fragmentState = rememberFragmentState()
|
||||||
var currentFragment by remember { mutableStateOf<ContactSelectionListFragment?>(null) }
|
var currentFragment by remember { mutableStateOf<ContactSelectionListFragment?>(null) }
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
|
|
||||||
Fragments.Fragment<ContactSelectionListFragment>(
|
Fragments.Fragment<ContactSelectionListFragment>(
|
||||||
arguments = fragmentArgs,
|
arguments = fragmentArgs,
|
||||||
@@ -188,6 +159,7 @@ private fun RecipientSearchResultsList(
|
|||||||
fragment.view?.setPadding(0, 0, 0, 0)
|
fragment.view?.setPadding(0, 0, 0, 0)
|
||||||
fragment.setUpCallbacks(
|
fragment.setUpCallbacks(
|
||||||
callbacks = callbacks,
|
callbacks = callbacks,
|
||||||
|
clearFocus = { focusManager.clearFocus() },
|
||||||
coroutineScope = coroutineScope
|
coroutineScope = coroutineScope
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -241,6 +213,7 @@ private fun RecipientSearchResultsList(
|
|||||||
|
|
||||||
private fun ContactSelectionListFragment.setUpCallbacks(
|
private fun ContactSelectionListFragment.setUpCallbacks(
|
||||||
callbacks: RecipientPickerCallbacks,
|
callbacks: RecipientPickerCallbacks,
|
||||||
|
clearFocus: () -> Unit,
|
||||||
coroutineScope: CoroutineScope
|
coroutineScope: CoroutineScope
|
||||||
) {
|
) {
|
||||||
val fragment: ContactSelectionListFragment = this
|
val fragment: ContactSelectionListFragment = this
|
||||||
@@ -302,9 +275,7 @@ private fun ContactSelectionListFragment.setUpCallbacks(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fragment.setOnRefreshListener { callbacks.refresh?.onRefresh() }
|
fragment.setOnRefreshListener { callbacks.refresh?.onRefresh() }
|
||||||
fragment.setScrollCallback {
|
fragment.setScrollCallback { clearFocus() }
|
||||||
fragment.view?.let { view -> ViewUtil.hideKeyboard(view.context, view) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun showItemContextMenu(
|
private suspend fun showItemContextMenu(
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.thoughtcrime.securesms.conversation
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.SearchBar
|
||||||
|
import androidx.compose.material3.SearchBarDefaults
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
|
import androidx.compose.material3.rememberSearchBarState
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.res.vectorResource
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import org.signal.core.ui.compose.DayNightPreviews
|
||||||
|
import org.signal.core.ui.compose.IconButtons.IconButton
|
||||||
|
import org.signal.core.ui.compose.theme.SignalTheme
|
||||||
|
import org.thoughtcrime.securesms.R
|
||||||
|
import org.thoughtcrime.securesms.components.compose.ProvideIncognitoKeyboard
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A search input field for finding recipients.
|
||||||
|
*
|
||||||
|
* Replaces [org.thoughtcrime.securesms.components.ContactFilterView].
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class, ExperimentalComposeUiApi::class)
|
||||||
|
@Composable
|
||||||
|
fun RecipientSearchBar(
|
||||||
|
hint: String = stringResource(R.string.RecipientSearchBar__search_name_or_number),
|
||||||
|
query: String,
|
||||||
|
onQueryChange: (String) -> Unit,
|
||||||
|
onSearch: (String) -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val state = rememberSearchBarState()
|
||||||
|
var keyboardOptions by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
KeyboardOptions(
|
||||||
|
keyboardType = KeyboardType.Text,
|
||||||
|
imeAction = ImeAction.Search
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ProvideIncognitoKeyboard(
|
||||||
|
enabled = TextSecurePreferences.isIncognitoKeyboardEnabled(LocalContext.current)
|
||||||
|
) {
|
||||||
|
SearchBar(
|
||||||
|
state = state,
|
||||||
|
inputField = {
|
||||||
|
TextField(
|
||||||
|
value = query,
|
||||||
|
onValueChange = onQueryChange,
|
||||||
|
placeholder = { Text(hint) },
|
||||||
|
singleLine = true,
|
||||||
|
shape = SearchBarDefaults.inputFieldShape,
|
||||||
|
colors = TextFieldDefaults.colors(
|
||||||
|
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant,
|
||||||
|
focusedIndicatorColor = Color.Transparent,
|
||||||
|
disabledIndicatorColor = Color.Transparent,
|
||||||
|
unfocusedIndicatorColor = Color.Transparent
|
||||||
|
),
|
||||||
|
keyboardOptions = keyboardOptions,
|
||||||
|
keyboardActions = KeyboardActions(
|
||||||
|
onSearch = { onSearch(query) }
|
||||||
|
),
|
||||||
|
trailingIcon = {
|
||||||
|
val modifier = Modifier.padding(end = 4.dp)
|
||||||
|
if (query.isNotEmpty()) {
|
||||||
|
ClearQueryButton(
|
||||||
|
onClearQuery = { onQueryChange("") },
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
KeyboardToggleButton(
|
||||||
|
keyboardType = keyboardOptions.keyboardType,
|
||||||
|
onKeyboardTypeChange = { keyboardOptions = keyboardOptions.copy(keyboardType = it) },
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = modifier
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun KeyboardToggleButton(
|
||||||
|
keyboardType: KeyboardType,
|
||||||
|
onKeyboardTypeChange: (KeyboardType) -> Unit = {},
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = {
|
||||||
|
onKeyboardTypeChange(
|
||||||
|
when (keyboardType) {
|
||||||
|
KeyboardType.Text -> KeyboardType.Phone
|
||||||
|
else -> KeyboardType.Text
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
when (keyboardType) {
|
||||||
|
KeyboardType.Text -> Icon(
|
||||||
|
imageVector = ImageVector.vectorResource(R.drawable.ic_number_pad_conversation_filter_24),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
contentDescription = stringResource(R.string.RecipientSearchBar_accessibility_switch_to_numeric_keyboard)
|
||||||
|
)
|
||||||
|
|
||||||
|
else -> Icon(
|
||||||
|
imageVector = ImageVector.vectorResource(R.drawable.ic_keyboard_24),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
contentDescription = stringResource(R.string.RecipientSearchBar_accessibility_switch_to_alphanumeric_keyboard)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ClearQueryButton(
|
||||||
|
onClearQuery: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = { onClearQuery() },
|
||||||
|
modifier = modifier
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = ImageVector.vectorResource(R.drawable.ic_x_conversation_filter_24),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurface,
|
||||||
|
contentDescription = stringResource(R.string.RecipientSearchBar_accessibility_clear_search)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@DayNightPreviews
|
||||||
|
private fun RecipientSearchBarPreview() = SignalTheme {
|
||||||
|
RecipientSearchBar(
|
||||||
|
query = "",
|
||||||
|
onQueryChange = {},
|
||||||
|
onSearch = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -4,9 +4,8 @@ import android.widget.EditText
|
|||||||
import androidx.appcompat.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since this value is only supported on API26+ we hard-code it here
|
* Since [android.view.inputmethod.EditorInfo.IME_FLAG_NO_PERSONALIZED_LEARNING] is only supported on API 26+ we hard-code it here
|
||||||
* to avoid issues with older versions. This mirrors the approach
|
* to avoid issues with older versions. This mirrors the approach taken by [org.thoughtcrime.securesms.components.ComposeText].
|
||||||
* taken by [org.thoughtcrime.securesms.components.ComposeText].
|
|
||||||
*/
|
*/
|
||||||
private const val INCOGNITO_KEYBOARD = 16777216
|
private const val INCOGNITO_KEYBOARD = 16777216
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
android:background="@android:color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:fontFamily="sans-serif"
|
android:fontFamily="sans-serif"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:hint="@string/ContactFilterView__search_name_or_number"
|
android:hint="@string/RecipientSearchBar__search_name_or_number"
|
||||||
android:inputType="textPersonName"
|
android:inputType="textPersonName"
|
||||||
android:lineSpacingExtra="6sp"
|
android:lineSpacingExtra="6sp"
|
||||||
android:textAppearance="@style/TextSecure.TitleTextStyle"
|
android:textAppearance="@style/TextSecure.TitleTextStyle"
|
||||||
|
|||||||
@@ -5931,8 +5931,14 @@
|
|||||||
<item quantity="other">%1$s are not Signal users</item>
|
<item quantity="other">%1$s are not Signal users</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
|
||||||
<!-- Hint text that shows in the recipient search box when no text is entered yet. -->
|
<!-- Hint text that shows in the recipient search bar when no text is entered yet. -->
|
||||||
<string name="ContactFilterView__search_name_or_number">Name, username or number</string>
|
<string name="RecipientSearchBar__search_name_or_number">Name, username or number</string>
|
||||||
|
<!-- Accessibility label for the button to switch to the numeric keyboard type. -->
|
||||||
|
<string name="RecipientSearchBar_accessibility_switch_to_numeric_keyboard">Switch to numeric keyboard</string>
|
||||||
|
<!-- Accessibility label for the button to switch to the alphanumeric keyboard type. -->
|
||||||
|
<string name="RecipientSearchBar_accessibility_switch_to_alphanumeric_keyboard">Switch to alphanumeric keyboard</string>
|
||||||
|
<!-- Accessibility label for the button to clear the search filter. -->
|
||||||
|
<string name="RecipientSearchBar_accessibility_clear_search">Reset search filter</string>
|
||||||
|
|
||||||
<!-- VoiceNotePlayerView -->
|
<!-- VoiceNotePlayerView -->
|
||||||
<string name="VoiceNotePlayerView__dot_s">· %1$s</string>
|
<string name="VoiceNotePlayerView__dot_s">· %1$s</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user