package com.suno.android.common_ui.components.bottom_sheet import android.content.Context import android.content.Intent import android.net.Uri import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.SheetState import androidx.compose.material3.SheetValue import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ClipboardManager import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.unit.dp import com.suno.android.common_core_utils.extensions.xCheckIsAppInstalled import com.suno.android.common_ui.components.buttons.ShareButton import com.suno.android.common_ui.components.omni.CircleIconButton import com.suno.android.common_ui.theme.ExtendedTheme import com.suno.android.common_ui.theme.SunoTheme import kotlinx.collections.immutable.persistentListOf import java.net.URLEncoder import com.suno.android.common_res.R as CommonResR sealed interface SharePlatformConstants { sealed class Link( val backendValue: String, ) : SharePlatformConstants { data object Copy : Link("copy-link") data object X : Link("x") data object Instagram : Link("instagram") data object Email : Link("email") data object Facebook : Link("facebook") data object WhatsApp : Link("whatsapp") data object Messages : Link("messages") data object More : Link("more") } data object Video : SharePlatformConstants } // todo: figure out how this can be put into some vm/manager/mediator class to have a SSOT for the logic fun handleShareClicked( platform: SharePlatformConstants.Link, shareLink: String, context: Context, clipboardManager: ClipboardManager, ) { when (platform) { SharePlatformConstants.Link.Copy -> { clipboardManager.setText(AnnotatedString(shareLink)) } SharePlatformConstants.Link.X -> { val encodedUrl = URLEncoder.encode(shareLink, "UTF-8") val text = context.getString(CommonResR.string.share_song_subject) + " $encodedUrl" val twitterIntent = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("twitter://post?message=$text") setPackage("com.twitter.android") } fun webIntentFallback() { val webIntent = Intent( Intent.ACTION_VIEW, Uri.parse("https://x.com/intent/tweet?text=$text"), ) context.startActivity(webIntent) } try { if (twitterIntent.resolveActivity(context.packageManager) != null) { context.startActivity(twitterIntent) } else { webIntentFallback() } } catch (e: Exception) { webIntentFallback() } } SharePlatformConstants.Link.Instagram -> { // figure this out. instagram doesnt support direct text sharing, they require media like image or video } SharePlatformConstants.Link.Email -> { val text = context.getString(CommonResR.string.share_song_message) + "\n\n$shareLink" val intent = Intent(Intent.ACTION_SENDTO).apply { data = Uri.parse("mailto:") putExtra( Intent.EXTRA_SUBJECT, context.getString(CommonResR.string.share_song_subject), ) putExtra(Intent.EXTRA_TEXT, text) } context.startActivity(intent) } SharePlatformConstants.Link.Facebook -> { val facebookIntent = Intent(Intent.ACTION_SEND).apply { type = "text/plain" setPackage("com.facebook.katana") putExtra(Intent.EXTRA_TEXT, shareLink) } val webIntent = Intent( Intent.ACTION_VIEW, Uri.parse( "https://www.facebook.com/sharer/sharer.php?u=" + URLEncoder.encode( shareLink, "UTF-8", ), ), ) try { if (facebookIntent.resolveActivity(context.packageManager) != null) { context.startActivity(facebookIntent) } else { context.startActivity(webIntent) } } catch (e: Exception) { context.startActivity(webIntent) } } SharePlatformConstants.Link.WhatsApp -> { try { val text = context.getString(CommonResR.string.share_song_subject) + "\n\n$shareLink" val intent = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("whatsapp://send?text=$text") } context.startActivity(intent) } catch (e: Exception) { Toast.makeText( context, context.getString(CommonResR.string.app_not_installed), Toast.LENGTH_SHORT, ).show() e.printStackTrace() } } SharePlatformConstants.Link.Messages -> { val text = context.getString(CommonResR.string.share_song_subject) + " $shareLink" val intent = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("sms:") putExtra("sms_body", text) } context.startActivity(intent) } SharePlatformConstants.Link.More -> { val sendIntent: Intent = Intent().apply { action = Intent.ACTION_SEND type = "text/plain" putExtra(Intent.EXTRA_TEXT, shareLink) } val shareIntent = Intent.createChooser(sendIntent, null) context.startActivity(shareIntent) } } } data class SharePlatform( val packageName: String? = null, val platformCode: SharePlatformConstants, val label: String, val contentDescription: String, val vectorDrawableResId: Int, val iconColor: Color? = null, ) @Composable fun ShareLinkBottomSheet( sheetState: SheetState = rememberModalBottomSheetState(true), isShowShareVideoGateEnabled: Boolean = false, onDismiss: () -> Unit = {}, onShareClicked: (platform: SharePlatformConstants) -> Unit = {}, ) { val context = LocalContext.current val sharePlatformList = persistentListOf( SharePlatform( label = stringResource(CommonResR.string.share_video), platformCode = SharePlatformConstants.Video, contentDescription = stringResource(CommonResR.string.share_video), vectorDrawableResId = com.suno.android.common_ui.R.drawable.video, ), SharePlatform( label = stringResource(CommonResR.string.copy_link), platformCode = SharePlatformConstants.Link.Copy, contentDescription = stringResource(CommonResR.string.copy_link), vectorDrawableResId = com.suno.android.common_ui.R.drawable.link, ), SharePlatform( label = stringResource(CommonResR.string.whatsapp), packageName = "com.whatsapp", platformCode = SharePlatformConstants.Link.WhatsApp, contentDescription = stringResource(CommonResR.string.whatsapp), vectorDrawableResId = com.suno.android.common_ui.R.drawable.whatsapp, iconColor = Color(0xFF25D366), ), SharePlatform( label = stringResource(CommonResR.string.messages), platformCode = SharePlatformConstants.Link.Messages, contentDescription = stringResource(CommonResR.string.messages), vectorDrawableResId = com.suno.android.common_ui.R.drawable.message, ), SharePlatform( label = stringResource(CommonResR.string.email), packageName = null, platformCode = SharePlatformConstants.Link.Email, contentDescription = stringResource(CommonResR.string.email), vectorDrawableResId = com.suno.android.common_ui.R.drawable.email, ), SharePlatform( label = stringResource(CommonResR.string.facebook), packageName = "com.facebook.katana", platformCode = SharePlatformConstants.Link.Facebook, contentDescription = stringResource(CommonResR.string.facebook), vectorDrawableResId = com.suno.android.common_ui.R.drawable.facebook, iconColor = Color(0xFF1877F2), ), SharePlatform( label = stringResource(CommonResR.string.twitter), packageName = "com.twitter.android", platformCode = SharePlatformConstants.Link.X, contentDescription = stringResource(CommonResR.string.twitter), vectorDrawableResId = com.suno.android.common_ui.R.drawable.x, iconColor = Color.Black, ), SharePlatform( label = stringResource(CommonResR.string.show_more), packageName = null, platformCode = SharePlatformConstants.Link.More, contentDescription = stringResource(CommonResR.string.show_more), vectorDrawableResId = com.suno.android.common_ui.R.drawable.ellipsis_vertical, ), ) val filteredSharePlatforms = sharePlatformList.filter { if (it.platformCode is SharePlatformConstants.Video && !isShowShareVideoGateEnabled) return@filter false if (it.packageName == null) return@filter true return@filter context.xCheckIsAppInstalled(it.packageName) } ModalBottomSheet( onDismissRequest = onDismiss, sheetState = sheetState, dragHandle = null, ) { val statusBarHeightDp = with(LocalDensity.current) { WindowInsets.statusBars.getTop(LocalDensity.current).toDp() } Column( modifier = Modifier .fillMaxWidth() .defaultMinSize(minHeight = 128.dp) .heightIn( max = (LocalConfiguration.current.screenHeightDp - statusBarHeightDp.value).dp, ), ) { Box( modifier = Modifier .fillMaxWidth() .height(64.dp), ) { Text( text = stringResource(CommonResR.string.share), textAlign = TextAlign.Center, style = ExtendedTheme.typography.largeBody, modifier = Modifier.align(Alignment.Center), ) CircleIconButton( modifier = Modifier .align(Alignment.CenterEnd) .padding(16.dp), vectorDrawableResId = com.suno.android.common_ui.R.drawable.close, contentDescription = "Close", onClick = onDismiss, ) } LazyVerticalGrid( modifier = Modifier .fillMaxWidth() .padding(bottom = 16.dp), columns = GridCells.Adaptive(minSize = 100.dp), contentPadding = PaddingValues(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { items(filteredSharePlatforms) { shareItem -> ShareButton( text = shareItem.label, onClick = { onShareClicked(shareItem.platformCode) }, contentDescription = shareItem.contentDescription, vectorDrawableResId = shareItem.vectorDrawableResId, iconColor = shareItem.iconColor ?: Color(0xFF333333), ) } } } } } @PreviewLightDark @Composable private fun ShareLinkBottomSheet_Preview() { val density = LocalDensity.current SunoTheme { ShareLinkBottomSheet( sheetState = remember { SheetState( skipPartiallyExpanded = true, density = density, initialValue = SheetValue.Expanded, ) }, ) } }