diff --git a/java/res/drawable/push_pin.xml b/java/res/drawable/push_pin.xml new file mode 100644 index 000000000..0cfddf022 --- /dev/null +++ b/java/res/drawable/push_pin.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt index 9e1c7fe86..6a02aac54 100644 --- a/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt +++ b/java/src/org/futo/inputmethod/latin/uix/actions/ClipboardHistoryAction.kt @@ -4,8 +4,12 @@ import android.content.ClipDescription import android.content.ClipboardManager import android.content.Context import android.net.Uri +import android.os.Build import android.widget.Toast import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -25,7 +29,9 @@ import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -45,6 +51,7 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.Json import org.futo.inputmethod.latin.R +import org.futo.inputmethod.latin.common.Constants import org.futo.inputmethod.latin.uix.Action import org.futo.inputmethod.latin.uix.ActionWindow import org.futo.inputmethod.latin.uix.PersistentActionState @@ -87,14 +94,21 @@ data class ClipboardEntry( val mimeTypes: List ) +@OptIn(ExperimentalFoundationApi::class) @Composable -fun ClipboardEntryView(clipboardEntry: ClipboardEntry, onPaste: (ClipboardEntry) -> Unit, onRemove: (ClipboardEntry) -> Unit, onPin: (ClipboardEntry) -> Unit) { +fun ClipboardEntryView(modifier: Modifier, clipboardEntry: ClipboardEntry, onPaste: (ClipboardEntry) -> Unit, onRemove: (ClipboardEntry) -> Unit, onPin: (ClipboardEntry) -> Unit) { Surface( color = MaterialTheme.colorScheme.surfaceVariant, border = BorderStroke(2.dp, MaterialTheme.colorScheme.outlineVariant), - modifier = Modifier.padding(2.dp), + modifier = modifier.padding(2.dp) + .combinedClickable( + interactionSource = remember { MutableInteractionSource() }, + indication = androidx.compose.material.ripple.rememberRipple(), + enabled = true, + onClick = { onPaste(clipboardEntry) }, + onLongClick = { onPin(clipboardEntry) } + ), shape = RoundedCornerShape(8.dp), - onClick = { onPaste(clipboardEntry) } ) { Column { Row(modifier = Modifier.padding(0.dp)) { @@ -102,12 +116,14 @@ fun ClipboardEntryView(clipboardEntry: ClipboardEntry, onPaste: (ClipboardEntry) onPin(clipboardEntry) }, modifier = Modifier.size(32.dp)) { Icon( - painterResource(id = R.drawable.unlock), contentDescription = "Pin", - tint = if (clipboardEntry.pinned) { + painterResource(id = R.drawable.push_pin), + contentDescription = "Pin", + tint = if(clipboardEntry.pinned) { MaterialTheme.colorScheme.onSurfaceVariant } else { - MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.25f) - }, modifier = Modifier.size(16.dp) + MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f) + }, + modifier = Modifier.size(16.dp) ) } @@ -145,7 +161,7 @@ fun ClipboardEntryViewPreview() { horizontalArrangement = Arrangement.spacedBy(4.dp), ) { items(sampleText.size) { - ClipboardEntryView(clipboardEntry = ClipboardEntry(0L, false, sampleText[it], null, listOf()), onPin = {}, onPaste = {}, onRemove = {}) + ClipboardEntryView(modifier = Modifier, clipboardEntry = ClipboardEntry(0L, it % 2 == 0, sampleText[it], null, listOf()), onPin = {}, onPaste = {}, onRemove = {}) } } } @@ -176,7 +192,11 @@ class ClipboardHistoryManager(val context: Context, val coroutineScope: Lifecycl val text = clip?.getItemAt(0)?.coerceToText(context)?.toString() val uri = clip?.getItemAt(0)?.uri - val timestamp = clip?.description?.timestamp ?: System.currentTimeMillis() + val timestamp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + clip?.description?.timestamp + } else { + null + } ?: System.currentTimeMillis() val mimeTypes = List(clip?.description?.mimeTypeCount ?: 0) { clip?.description?.getMimeType(it) @@ -309,6 +329,7 @@ class ClipboardHistoryManager(val context: Context, val coroutineScope: Lifecycl } +@OptIn(ExperimentalFoundationApi::class) val ClipboardHistoryAction = Action( icon = R.drawable.clipboard, name = R.string.clipboard_manager_action_title, @@ -330,6 +351,7 @@ val ClipboardHistoryAction = Action( @Composable override fun WindowContents(keyboardShown: Boolean) { + val view = LocalView.current val clipboardHistory = useDataStore(ClipboardHistoryEnabled, blocking = true) if(!clipboardHistory.value) { ScrollableList { @@ -349,10 +371,17 @@ val ClipboardHistoryAction = Action( verticalItemSpacing = 4.dp, horizontalArrangement = Arrangement.spacedBy(4.dp), ) { - items(clipboardHistoryManager.clipboardHistory.size) { r_i -> + items(clipboardHistoryManager.clipboardHistory.size, key = { r_i -> val i = clipboardHistoryManager.clipboardHistory.size - r_i - 1 val entry = clipboardHistoryManager.clipboardHistory[i] - ClipboardEntryView(clipboardEntry = entry, onPaste = { + + entry.text ?: i + }) { r_i -> + val i = clipboardHistoryManager.clipboardHistory.size - r_i - 1 + val entry = clipboardHistoryManager.clipboardHistory[i] + ClipboardEntryView( + modifier = Modifier.animateItemPlacement(), + clipboardEntry = entry, onPaste = { if (it.uri != null) { if (!manager.typeUri(it.uri, it.mimeTypes)) { val toast = Toast.makeText( @@ -366,10 +395,13 @@ val ClipboardHistoryAction = Action( manager.typeText(it.text) } clipboardHistoryManager.onPaste(it) + manager.performHapticAndAudioFeedback(Constants.CODE_OUTPUT_TEXT, view) }, onRemove = { clipboardHistoryManager.onRemove(it) + manager.performHapticAndAudioFeedback(Constants.CODE_TAB, view) }, onPin = { clipboardHistoryManager.onPin(it) + manager.performHapticAndAudioFeedback(Constants.CODE_TAB, view) }) } }