Skip to content

Commit ba59f2b

Browse files
committed
Merge remote-tracking branch 'origin/dev' into dev
2 parents 1bf1847 + 08a65a7 commit ba59f2b

7 files changed

Lines changed: 126 additions & 33 deletions

File tree

‎app/src/main/java/dev/typetype/android/core/ui/share/ShareUrls.kt‎

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
package dev.typetype.android.core.ui.share
22

3+
import android.app.AlertDialog
4+
import android.content.Context
5+
import android.content.Intent
6+
import android.graphics.Color
7+
import android.graphics.drawable.ColorDrawable
8+
import android.view.Gravity
9+
import android.view.View
10+
import android.view.ViewGroup
11+
import android.widget.BaseAdapter
12+
import android.widget.ImageView
13+
import android.widget.LinearLayout
14+
import android.widget.TextView
315
import androidx.compose.runtime.compositionLocalOf
16+
import dev.typetype.android.R
17+
import dev.typetype.android.domain.navigation.resolveIncomingVideoUrl
418
import dev.typetype.android.domain.navigation.toPublicWatchParameter
519
import java.net.URI
620
import java.net.URLEncoder
@@ -19,6 +33,92 @@ fun buildShareUrl(serverBaseUrl: String?, videoUrl: String): String {
1933
return "$origin/watch?v=$encoded"
2034
}
2135

36+
fun buildSourceShareUrl(videoUrl: String): String =
37+
resolveIncomingVideoUrl(videoUrl) ?: videoUrl.trim()
38+
39+
fun showShareChooser(
40+
context: Context,
41+
serverBaseUrl: String?,
42+
videoUrl: String,
43+
chooserTitle: String,
44+
) {
45+
val sourceUrl = buildSourceShareUrl(videoUrl)
46+
val typeTypeUrl = buildShareUrl(serverBaseUrl, videoUrl)
47+
val choices = buildList {
48+
add(
49+
ShareChoice(
50+
context.getString(R.string.video_menu_share_typetype),
51+
typeTypeUrl,
52+
android.R.drawable.ic_menu_share,
53+
),
54+
)
55+
providerChoice(context, sourceUrl)?.takeIf { it.url != typeTypeUrl }?.let(::add)
56+
}
57+
AlertDialog.Builder(context)
58+
.setTitle(R.string.video_menu_share)
59+
.setAdapter(ShareChoiceAdapter(context, choices)) { _, which ->
60+
val intent = Intent(Intent.ACTION_SEND).apply {
61+
type = "text/plain"
62+
putExtra(Intent.EXTRA_TEXT, choices[which].url)
63+
}
64+
context.startActivity(Intent.createChooser(intent, chooserTitle))
65+
}
66+
.show()
67+
}
68+
69+
private data class ShareChoice(val label: String, val url: String, val icon: Int)
70+
71+
private fun providerChoice(context: Context, sourceUrl: String): ShareChoice? {
72+
val provider = sourceUrl.lowercase()
73+
return when {
74+
"youtube.com" in provider || "youtu.be" in provider -> ShareChoice(
75+
context.getString(R.string.video_menu_share_source, "YouTube"),
76+
sourceUrl,
77+
R.drawable.ic_service_youtube,
78+
)
79+
"nicovideo.jp" in provider || "nico.ms" in provider -> ShareChoice(
80+
context.getString(R.string.video_menu_share_source, "NicoNico"),
81+
sourceUrl,
82+
R.drawable.ic_service_niconico,
83+
)
84+
"bilibili.com" in provider || "b23.tv" in provider -> ShareChoice(
85+
context.getString(R.string.video_menu_share_source, "BiliBili"),
86+
sourceUrl,
87+
R.drawable.ic_service_bilibili,
88+
)
89+
else -> null
90+
}
91+
}
92+
93+
private class ShareChoiceAdapter(
94+
private val context: Context,
95+
private val choices: List<ShareChoice>,
96+
) : BaseAdapter() {
97+
override fun getCount(): Int = choices.size
98+
override fun getItem(position: Int): ShareChoice = choices[position]
99+
override fun getItemId(position: Int): Long = position.toLong()
100+
101+
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
102+
val choice = getItem(position)
103+
val density = context.resources.displayMetrics.density
104+
val row = (convertView as? LinearLayout) ?: LinearLayout(context).apply {
105+
orientation = LinearLayout.HORIZONTAL
106+
gravity = Gravity.CENTER_VERTICAL
107+
background = ColorDrawable(Color.TRANSPARENT)
108+
val icon = ImageView(context)
109+
addView(icon, LinearLayout.LayoutParams((28 * density).toInt(), (28 * density).toInt()))
110+
val label = TextView(context).apply {
111+
setTextAppearance(android.R.style.TextAppearance_Material_Body1)
112+
}
113+
addView(label, LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f))
114+
}
115+
row.setPadding((20 * density).toInt(), (12 * density).toInt(), (20 * density).toInt(), (12 * density).toInt())
116+
(row.getChildAt(0) as ImageView).setImageResource(choice.icon)
117+
(row.getChildAt(1) as TextView).text = choice.label
118+
return row
119+
}
120+
}
121+
22122
fun buildImageUrl(serverBaseUrl: String?, imageUrl: String): String {
23123
val source = imageUrl.trim().let { value ->
24124
if (value.startsWith("httpss://")) "https://${value.removePrefix("httpss://")}" else value

‎app/src/main/java/dev/typetype/android/feature/library/LibraryPlaylistContextTab.kt‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.typetype.android.feature.library
22

3-
import android.content.Intent
43
import androidx.compose.foundation.layout.PaddingValues
54
import androidx.compose.foundation.layout.fillMaxSize
65
import androidx.compose.foundation.lazy.grid.GridCells
@@ -24,7 +23,7 @@ import dev.typetype.android.core.ui.components.LocalAppSnackbarHost
2423
import dev.typetype.android.core.ui.components.PlaylistVideoActionsSheet
2524
import dev.typetype.android.core.ui.components.PlaylistVideoCard
2625
import dev.typetype.android.core.ui.share.LocalServerBaseUrl
27-
import dev.typetype.android.core.ui.share.buildShareUrl
26+
import dev.typetype.android.core.ui.share.showShareChooser
2827
import dev.typetype.android.domain.library.PlaylistVideo
2928
import dev.typetype.android.feature.library.components.rememberVideoMetas
3029
import dev.typetype.android.feature.menu.VideoMenuEvent
@@ -104,11 +103,7 @@ fun PlaylistContextTab(
104103
onRemoveFromList = { onRemove(video) },
105104
onToggleWatched = { onToggleWatched(video, video.url in watchedUrls) },
106105
onShare = {
107-
val intent = Intent(Intent.ACTION_SEND).apply {
108-
type = "text/plain"
109-
putExtra(Intent.EXTRA_TEXT, buildShareUrl(serverBaseUrl, video.url))
110-
}
111-
context.startActivity(Intent.createChooser(intent, shareChooserTitle))
106+
showShareChooser(context, serverBaseUrl, video.url, shareChooserTitle)
112107
},
113108
onBlockVideo = { onBlockVideo(video) },
114109
onDismiss = { pendingMenu = null },

‎app/src/main/java/dev/typetype/android/feature/library/playlist/PlaylistScreen.kt‎

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.typetype.android.feature.library.playlist
22

3-
import android.content.Intent
43
import androidx.compose.foundation.layout.Box
54
import androidx.compose.foundation.layout.Column
65
import androidx.compose.foundation.layout.PaddingValues
@@ -41,6 +40,7 @@ import dev.typetype.android.feature.menu.VideoMenuHandlerViewModel
4140
import dev.typetype.android.feature.menu.blockVideoUrl
4241
import dev.typetype.android.feature.menu.removeFromPlaylist
4342
import dev.typetype.android.feature.menu.toggleWatchedUrl
43+
import dev.typetype.android.core.ui.share.showShareChooser
4444

4545
@Composable
4646
fun PlaylistRoute(
@@ -253,17 +253,7 @@ internal fun PlaylistScreen(
253253
)
254254
},
255255
onShare = {
256-
val intent = Intent(Intent.ACTION_SEND).apply {
257-
type = "text/plain"
258-
putExtra(
259-
Intent.EXTRA_TEXT,
260-
dev.typetype.android.core.ui.share.buildShareUrl(
261-
serverBaseUrl,
262-
video.url,
263-
),
264-
)
265-
}
266-
context.startActivity(Intent.createChooser(intent, shareChooserTitle))
256+
showShareChooser(context, serverBaseUrl, video.url, shareChooserTitle)
267257
},
268258
onBlockVideo = { menuVm.blockVideoUrl(video.url) },
269259
onDismiss = { pendingMenu = null },

‎app/src/main/java/dev/typetype/android/feature/menu/VideoMenuHost.kt‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.typetype.android.feature.menu
22

3-
import android.content.Intent
43
import androidx.compose.material3.SnackbarDuration
54
import androidx.compose.material3.SnackbarHostState
65
import androidx.compose.runtime.Composable
@@ -19,7 +18,7 @@ import dev.typetype.android.core.ui.components.LocalAppSnackbarHost
1918
import dev.typetype.android.core.ui.components.VideoMenuAction
2019
import dev.typetype.android.core.ui.components.VideoMenuItemState
2120
import dev.typetype.android.core.ui.share.LocalServerBaseUrl
22-
import dev.typetype.android.core.ui.share.buildShareUrl
21+
import dev.typetype.android.core.ui.share.showShareChooser
2322
import dev.typetype.android.domain.feed.Video
2423
import dev.typetype.android.domain.navigation.canonicalVideoIdentity
2524
import dev.typetype.android.domain.actions.titleMatchesBlockedKeyword
@@ -126,11 +125,7 @@ fun rememberVideoMenuScope(
126125
)
127126
VideoMenuAction.Download -> downloadVideo = video
128127
VideoMenuAction.Share -> {
129-
val intent = Intent(Intent.ACTION_SEND).apply {
130-
type = "text/plain"
131-
putExtra(Intent.EXTRA_TEXT, buildShareUrl(serverBaseUrl, video.url))
132-
}
133-
context.startActivity(Intent.createChooser(intent, shareChooserTitle))
128+
showShareChooser(context, serverBaseUrl, video.url, shareChooserTitle)
134129
}
135130
VideoMenuAction.OpenChannel -> onOpenChannel(video.uploaderUrl)
136131
VideoMenuAction.BlockVideo -> viewModel.blockVideo(video)

‎app/src/main/java/dev/typetype/android/feature/player/PlayerInteractionRow.kt‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package dev.typetype.android.feature.player
22

3-
import android.content.Intent
43
import androidx.compose.foundation.BorderStroke
54
import androidx.compose.foundation.layout.Arrangement
65
import androidx.compose.foundation.layout.Row
@@ -30,7 +29,7 @@ import androidx.compose.ui.res.stringResource
3029
import androidx.compose.ui.unit.dp
3130
import dev.typetype.android.R
3231
import dev.typetype.android.core.ui.share.LocalServerBaseUrl
33-
import dev.typetype.android.core.ui.share.buildShareUrl
32+
import dev.typetype.android.core.ui.share.showShareChooser
3433

3534
@Composable
3635
fun PlayerInteractionRow(
@@ -104,11 +103,7 @@ fun PlayerInteractionRow(
104103
icon = Icons.Filled.Share,
105104
contentDescription = stringResource(R.string.video_menu_share),
106105
onClick = {
107-
val intent = Intent(Intent.ACTION_SEND).apply {
108-
type = "text/plain"
109-
putExtra(Intent.EXTRA_TEXT, buildShareUrl(serverBaseUrl, shareUrl))
110-
}
111-
context.startActivity(Intent.createChooser(intent, shareChooserTitle))
106+
showShareChooser(context, serverBaseUrl, shareUrl, shareChooserTitle)
112107
},
113108
)
114109
}

‎app/src/main/res/values/strings.xml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,8 @@
921921
<string name="video_menu_block_video">Block video</string>
922922
<string name="video_menu_block_channel">Block channel</string>
923923
<string name="video_menu_share_chooser">Share via</string>
924+
<string name="video_menu_share_typetype">Share TypeType link</string>
925+
<string name="video_menu_share_source">Share %1$s link</string>
924926
<string name="video_more_actions">Video actions</string>
925927
<string name="video_open_channel_accessibility">Open %1$s channel</string>
926928
<string name="video_open_accessibility">Open %1$s</string>

‎app/src/test/java/dev/typetype/android/core/ui/share/ShareUrlsTest.kt‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,22 @@ import org.junit.Test
55

66
class ShareUrlsTest {
77

8+
@Test
9+
fun sharesCanonicalProviderSourceLinks() {
10+
assertEquals(
11+
"https://www.youtube.com/watch?v=AbCdEfGhI_1",
12+
buildSourceShareUrl("AbCdEfGhI_1"),
13+
)
14+
assertEquals(
15+
"https://www.bilibili.com/video/BV1UbX3B2EZQ?p=3",
16+
buildSourceShareUrl("BV1UbX3B2EZQ?p=3"),
17+
)
18+
assertEquals(
19+
"https://www.nicovideo.jp/watch/sm46525483",
20+
buildSourceShareUrl("sm46525483"),
21+
)
22+
}
23+
824
@Test
925
fun sharesCompactPublicWatchRoutes() {
1026
assertEquals(

0 commit comments

Comments
 (0)