@@ -12,16 +12,20 @@ import androidx.compose.animation.ExperimentalAnimationApi
1212import androidx.compose.animation.fadeIn
1313import androidx.compose.animation.fadeOut
1414import androidx.compose.animation.togetherWith
15+ import androidx.compose.foundation.Image
1516import androidx.compose.foundation.background
17+ import androidx.compose.foundation.border
1618import androidx.compose.foundation.clickable
1719import androidx.compose.foundation.layout.Box
1820import androidx.compose.foundation.layout.Column
1921import androidx.compose.foundation.layout.Spacer
2022import androidx.compose.foundation.layout.fillMaxSize
23+ import androidx.compose.foundation.layout.fillMaxWidth
2124import androidx.compose.foundation.layout.height
2225import androidx.compose.foundation.layout.navigationBarsPadding
2326import androidx.compose.foundation.layout.padding
2427import androidx.compose.foundation.layout.size
28+ import androidx.compose.foundation.shape.CircleShape
2529import androidx.compose.foundation.shape.RoundedCornerShape
2630import androidx.compose.material.icons.Icons
2731import androidx.compose.material.icons.rounded.CheckCircle
@@ -34,18 +38,27 @@ import androidx.compose.material3.Surface
3438import androidx.compose.material3.Text
3539import androidx.compose.runtime.Composable
3640import androidx.compose.runtime.LaunchedEffect
41+ import androidx.compose.runtime.collectAsState
3742import androidx.compose.runtime.getValue
3843import androidx.compose.runtime.mutableStateOf
3944import androidx.compose.runtime.remember
4045import androidx.compose.runtime.setValue
4146import androidx.compose.ui.Alignment
4247import androidx.compose.ui.Modifier
48+ import androidx.compose.ui.draw.clip
49+ import androidx.compose.ui.layout.ContentScale
50+ import androidx.compose.ui.res.painterResource
51+ import androidx.compose.ui.res.stringResource
4352import androidx.compose.ui.text.font.FontWeight
4453import androidx.compose.ui.tooling.preview.Preview
4554import androidx.compose.ui.unit.dp
55+ import com.sameerasw.airsync.R
56+ import com.sameerasw.airsync.data.local.DataStoreManager
57+ import com.sameerasw.airsync.domain.model.ConnectedDevice
4658import com.sameerasw.airsync.ui.theme.AirSyncTheme
4759import com.sameerasw.airsync.utils.ClipboardSyncManager
4860import com.sameerasw.airsync.utils.ClipboardUtil
61+ import com.sameerasw.airsync.utils.DevicePreviewResolver
4962import kotlinx.coroutines.delay
5063
5164class ClipboardActionActivity : ComponentActivity () {
@@ -88,12 +101,19 @@ class ClipboardActionActivity : ComponentActivity() {
88101
89102@Composable
90103fun ClipboardActionScreen (hasWindowFocus : Boolean , onFinished : () -> Unit ) {
104+ val context = androidx.compose.ui.platform.LocalContext .current
105+ val dataStoreManager = remember { DataStoreManager .getInstance(context) }
106+ val connectedDevice by dataStoreManager.getLastConnectedDevice().collectAsState(initial = null )
107+
91108 var uiState by remember { mutableStateOf<ClipboardUiState >(ClipboardUiState .Loading ) }
92109 var hasAttemptedSync by remember { mutableStateOf(false ) }
93110
94- ClipboardActionScreenContent (uiState = uiState, onFinished = onFinished)
111+ ClipboardActionScreenContent (
112+ uiState = uiState,
113+ connectedDevice = connectedDevice,
114+ onFinished = onFinished
115+ )
95116
96- val context = androidx.compose.ui.platform.LocalContext .current
97117 LaunchedEffect (hasWindowFocus) {
98118 if (hasWindowFocus && ! hasAttemptedSync) {
99119 hasAttemptedSync = true
@@ -125,7 +145,11 @@ fun ClipboardActionScreen(hasWindowFocus: Boolean, onFinished: () -> Unit) {
125145
126146@OptIn(ExperimentalAnimationApi ::class , ExperimentalMaterial3ExpressiveApi ::class )
127147@Composable
128- fun ClipboardActionScreenContent (uiState : ClipboardUiState , onFinished : () -> Unit ) {
148+ fun ClipboardActionScreenContent (
149+ uiState : ClipboardUiState ,
150+ connectedDevice : ConnectedDevice ? ,
151+ onFinished : () -> Unit
152+ ) {
129153 // Transparent background that dismisses on click
130154 Box (
131155 modifier = Modifier
@@ -145,7 +169,7 @@ fun ClipboardActionScreenContent(uiState: ClipboardUiState, onFinished: () -> Un
145169 ) {
146170 Box (
147171 modifier = Modifier
148- .padding(32 .dp),
172+ .padding(24 .dp),
149173 contentAlignment = Alignment .Center
150174 ) {
151175 AnimatedContent (
@@ -156,47 +180,75 @@ fun ClipboardActionScreenContent(uiState: ClipboardUiState, onFinished: () -> Un
156180 Column (
157181 horizontalAlignment = Alignment .CenterHorizontally
158182 ) {
159- when (state) {
160- is ClipboardUiState .Loading -> {
161- LoadingIndicator (
162- modifier = Modifier .size(48 .dp),
163- color = MaterialTheme .colorScheme.primary
164- )
165- Spacer (modifier = Modifier .height(16 .dp))
166- Text (
167- text = " Syncing..." ,
168- style = MaterialTheme .typography.titleMedium,
169- fontWeight = FontWeight .Medium
170- )
171- }
172- is ClipboardUiState .Success -> {
173- Icon (
174- imageVector = Icons .Rounded .CheckCircle ,
175- contentDescription = " Success" ,
176- modifier = Modifier .size(48 .dp)
177- )
178- Spacer (modifier = Modifier .height(16 .dp))
179- Text (
180- text = " Clipboard Sent!" ,
181- style = MaterialTheme .typography.titleMedium,
182- fontWeight = FontWeight .Bold
183- )
184- }
185- is ClipboardUiState .Error -> {
186- Icon (
187- imageVector = Icons .Rounded .Error ,
188- contentDescription = " Error" ,
189- tint = MaterialTheme .colorScheme.error,
190- modifier = Modifier .size(48 .dp)
191- )
192- Spacer (modifier = Modifier .height(16 .dp))
193- Text (
194- text = state.message,
195- style = MaterialTheme .typography.titleMedium,
196- color = MaterialTheme .colorScheme.error
197- )
183+ // Device preview with overlay
184+ Box (
185+ contentAlignment = Alignment .Center ,
186+ modifier = Modifier .padding(bottom = 16 .dp)
187+ ) {
188+ val previewRes = DevicePreviewResolver .getPreviewRes(connectedDevice)
189+ Image (
190+ painter = painterResource(id = previewRes),
191+ contentDescription = " Device Preview" ,
192+ modifier = Modifier .fillMaxWidth(0.9f ),
193+ contentScale = ContentScale .Fit
194+ )
195+
196+ Box (
197+ modifier = Modifier
198+ .size(56 .dp)
199+ .background(MaterialTheme .colorScheme.surfaceContainerHigh, shape = CircleShape )
200+ ) {
201+ // Overlay icon/indicator
202+ when (state) {
203+ is ClipboardUiState .Loading -> {
204+ LoadingIndicator (
205+ modifier = Modifier .size(56 .dp),
206+ color = MaterialTheme .colorScheme.primary
207+ )
208+ }
209+
210+ is ClipboardUiState .Success -> {
211+ Icon (
212+ imageVector = Icons .Rounded .CheckCircle ,
213+ contentDescription = " Success" ,
214+ modifier = Modifier .size(56 .dp),
215+ tint = MaterialTheme .colorScheme.primary
216+ )
217+ }
218+
219+ is ClipboardUiState .Error -> {
220+ Icon (
221+ imageVector = Icons .Rounded .Error ,
222+ contentDescription = " Error" ,
223+ tint = MaterialTheme .colorScheme.error,
224+ modifier = Modifier .size(56 .dp)
225+ )
226+ }
198227 }
199228 }
229+ }
230+
231+ Text (
232+ text = connectedDevice?.name ? : stringResource(R .string.your_mac),
233+ style = MaterialTheme .typography.titleMedium,
234+ color = MaterialTheme .colorScheme.onPrimary,
235+ modifier = Modifier
236+ .background(MaterialTheme .colorScheme.primary, shape = RoundedCornerShape (32 .dp))
237+ .padding(horizontal = 16 .dp, vertical = 4 .dp),
238+ )
239+
240+ Spacer (modifier = Modifier .height(8 .dp))
241+
242+ // Status Text
243+ Text (
244+ text = when (state) {
245+ is ClipboardUiState .Loading -> stringResource(R .string.sending)
246+ is ClipboardUiState .Success -> stringResource(R .string.clipboard_sent)
247+ is ClipboardUiState .Error -> stringResource(R .string.failed_to_send_clipboard)
248+ },
249+ style = MaterialTheme .typography.titleSmall,
250+ color = if (state is ClipboardUiState .Error ) MaterialTheme .colorScheme.error else MaterialTheme .colorScheme.onSurface
251+ )
200252 }
201253 }
202254 }
@@ -214,22 +266,22 @@ sealed class ClipboardUiState {
214266@Composable
215267private fun ClipboardActionScreenPreviewLoading () {
216268 AirSyncTheme {
217- ClipboardActionScreenContent (uiState = ClipboardUiState .Loading , onFinished = {})
269+ ClipboardActionScreenContent (uiState = ClipboardUiState .Loading , connectedDevice = null , onFinished = {})
218270 }
219271}
220272
221273@Preview(name = " Success State" , showBackground = true )
222274@Composable
223275private fun ClipboardActionScreenPreviewSuccess () {
224276 AirSyncTheme {
225- ClipboardActionScreenContent (uiState = ClipboardUiState .Success , onFinished = {})
277+ ClipboardActionScreenContent (uiState = ClipboardUiState .Success , connectedDevice = null , onFinished = {})
226278 }
227279}
228280
229281@Preview(name = " Error State" , showBackground = true )
230282@Composable
231283private fun ClipboardActionScreenPreviewError () {
232284 AirSyncTheme {
233- ClipboardActionScreenContent (uiState = ClipboardUiState .Error (" Failed to sync" ), onFinished = {})
285+ ClipboardActionScreenContent (uiState = ClipboardUiState .Error (" Failed to sync" ), connectedDevice = null , onFinished = {})
234286 }
235287}
0 commit comments