//deepseek
Here is a complete, single-file Android app built with Jetpack Compose. It displays a list of texts, and when you tap any text, a context menu pops up with options to copy, copy all, share, or translate the selected text.
```kotlin
package com.example.textcontextmenu
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Popup
import androidx.compose.ui.window.PopupProperties
import java.net.URLEncoder
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
TextContextMenuScreen()
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TextContextMenuScreen() {
// Sample list of texts to display
val sampleTexts = remember {
listOf(
TextItem(id = 1, content = "The only limit is your mind."),
TextItem(id = 2, content = "Jetpack Compose makes UI development a joy."),
TextItem(id = 3, content = "Kotlin is expressive and concise."),
TextItem(id = 4, content = "Context menus provide quick actions."),
TextItem(id = 5, content = "Copy, share, translate – all at your fingertips."),
TextItem(id = 6, content = "Always keep learning new things.")
)
}
// State to hold the currently selected text item and its anchor position
var contextMenuState by remember { mutableStateOf<ContextMenuState?>(null) }
// Map to store the screen coordinates of each text item (by id)
val itemCoordinates = remember { mutableStateMapOf<Int, Offset>() }
val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current
// Helper function to copy a single text
fun copyText(text: String) {
clipboardManager.setText(AnnotatedString(text))
android.widget.Toast.makeText(context, "Copied: $text", android.widget.Toast.LENGTH_SHORT).show()
}
// Helper function to copy all texts
fun copyAllTexts() {
val allText = sampleTexts.joinToString("\n") { it.content }
clipboardManager.setText(AnnotatedString(allText))
android.widget.Toast.makeText(context, "Copied all texts", android.widget.Toast.LENGTH_SHORT).show()
}
// Helper function to share a single text
fun shareText(text: String) {
val shareIntent = Intent(Intent.ACTION_SEND).apply {
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, text)
}
context.startActivity(Intent.createChooser(shareIntent, "Share via"))
}
// Helper function to translate text using Google Translate (opens browser)
fun translateText(text: String) {
val encodedText = URLEncoder.encode(text, "UTF-8")
val translateUrl = "https://translate.google.com/?text=$encodedText&sl=auto&tl=en"
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(translateUrl))
context.startActivity(intent)
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("Text Context Menu Demo") },
colors = TopAppBarDefaults.mediumTopAppBarColors()
)
}
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(sampleTexts, key = { it.id }) { item ->
// Each text item row
var itemPosition by remember { mutableStateOf(Offset.Zero) }
Card(
modifier = Modifier
.fillMaxWidth()
.onGloballyPositioned { coordinates ->
itemPosition = coordinates.positionInRoot()
// Update map with latest coordinates
itemCoordinates[item.id] = itemPosition
}
.clickable {
// Show context menu when item is tapped
val position = itemCoordinates[item.id] ?: itemPosition
contextMenuState = ContextMenuState(
text = item.content,
anchorPosition = position
)
},
shape = RoundedCornerShape(12.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Text(
text = item.content,
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
style = MaterialTheme.typography.bodyLarge
)
}
}
}
// Context Menu Popup
contextMenuState?.let { state ->
Popup(
alignment = Alignment.TopStart,
offset = IntOffset(state.anchorPosition.x.toInt(), state.anchorPosition.y.toInt() + 20), // slight vertical offset
onDismissRequest = { contextMenuState = null },
properties = PopupProperties(focusable = true)
) {
ContextMenuContent(
text = state.text,
onCopy = {
copyText(state.text)
contextMenuState = null
},
onCopyAll = {
copyAllTexts()
contextMenuState = null
},
onShare = {
shareText(state.text)
contextMenuState = null
},
onTranslate = {
translateText(state.text)
contextMenuState = null
},
onDismiss = { contextMenuState = null }
)
}
}
}
}
}
@Composable
fun ContextMenuContent(
text: String,
onCopy: () -> Unit,
onCopyAll: () -> Unit,
onShare: () -> Unit,
onTranslate: () -> Unit,
onDismiss: () -> Unit
) {
Card(
modifier = Modifier
.width(200.dp)
.shadow(8.dp, shape = RoundedCornerShape(8.dp)),
shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
) {
Column(
modifier = Modifier.padding(8.dp)
) {
// Optional: show the selected text preview
Text(
text = text.take(50) + if (text.length > 50) "..." else "",
style = MaterialTheme.typography.labelMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(bottom = 8.dp)
)
Divider(modifier = Modifier.padding(vertical = 4.dp))
// Action Items
ContextMenuItem(
text = "Copy",
onClick = onCopy
)
ContextMenuItem(
text = "Copy All",
onClick = onCopyAll
)
ContextMenuItem(
text = "Share",
onClick = onShare
)
ContextMenuItem(
text = "Translate",
onClick = onTranslate
)
Divider(modifier = Modifier.padding(vertical = 4.dp))
ContextMenuItem(
text = "Cancel",
onClick = onDismiss,
isDestructive = false
)
}
}
}
@Composable
fun ContextMenuItem(
text: String,
onClick: () -> Unit,
isDestructive: Boolean = false
) {
TextButton(
onClick = onClick,
modifier = Modifier.fillMaxWidth(),
colors = ButtonDefaults.textButtonColors(
contentColor = if (isDestructive) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.primary
)
) {
Text(
text = text,
modifier = Modifier.padding(vertical = 8.dp),
style = MaterialTheme.typography.bodyMedium
)
}
}
// Data class to hold the selected text and its anchor position
data class ContextMenuState(
val text: String,
val anchorPosition: Offset
)
// Data class for each text item in the list
data class TextItem(
val id: Int,
val content: String
)
```
How the App Works
1. Display Texts – A scrollable list of sample quotes or messages is shown using a LazyColumn.
2. Select a Text – Tapping any text card records its position on the screen and opens a popup context menu.
3. Context Menu Options:
· Copy – Copies the selected text to the clipboard.
· Copy All – Copies every text in the list (separated by newlines) to the clipboard.
· Share – Opens the Android share sheet with the selected text.
· Translate – Opens Google Translate in a browser with the selected text pre-filled.
· Cancel – Closes the menu without taking action.
4. Visual Feedback – Toast messages confirm copy actions, and the menu closes automatically after any action.
Key Implementation Details
· The popup is anchored to the exact location of the tapped text item using onGloballyPositioned and positionInRoot().
· All text operations use standard Android APIs: ClipboardManager for copy, Intent.ACTION_SEND for sharing, and an Intent.ACTION_VIEW to Google Translate.
· The UI follows Material 3 guidelines with cards, elevation, and a clean top app bar.