mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-13 22:00:30 +05:30
feat: share dialog redesign (including copy link feature)
This commit is contained in:
parent
47bae051ee
commit
fadd2d31af
@ -122,6 +122,7 @@ object PreferenceKeys {
|
|||||||
const val CLEAR_WATCH_HISTORY = "clear_watch_history"
|
const val CLEAR_WATCH_HISTORY = "clear_watch_history"
|
||||||
const val CLEAR_WATCH_POSITIONS = "clear_watch_positions"
|
const val CLEAR_WATCH_POSITIONS = "clear_watch_positions"
|
||||||
const val SHARE_WITH_TIME_CODE = "share_with_time_code"
|
const val SHARE_WITH_TIME_CODE = "share_with_time_code"
|
||||||
|
const val SELECTED_SHARE_HOST = "selected_share_host"
|
||||||
const val CONFIRM_UNSUBSCRIBE = "confirm_unsubscribing"
|
const val CONFIRM_UNSUBSCRIBE = "confirm_unsubscribing"
|
||||||
const val CLEAR_BOOKMARKS = "clear_bookmarks"
|
const val CLEAR_BOOKMARKS = "clear_bookmarks"
|
||||||
const val MAX_CONCURRENT_DOWNLOADS = "max_parallel_downloads"
|
const val MAX_CONCURRENT_DOWNLOADS = "max_parallel_downloads"
|
||||||
|
@ -4,6 +4,7 @@ import android.app.Dialog
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.addTextChangedListener
|
||||||
import androidx.fragment.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.constants.IntentData
|
import com.github.libretube.constants.IntentData
|
||||||
@ -13,11 +14,14 @@ import com.github.libretube.db.DatabaseHolder.Database
|
|||||||
import com.github.libretube.enums.ShareObjectType
|
import com.github.libretube.enums.ShareObjectType
|
||||||
import com.github.libretube.extensions.parcelable
|
import com.github.libretube.extensions.parcelable
|
||||||
import com.github.libretube.extensions.serializable
|
import com.github.libretube.extensions.serializable
|
||||||
|
import com.github.libretube.helpers.ClipboardHelper
|
||||||
import com.github.libretube.helpers.PreferenceHelper
|
import com.github.libretube.helpers.PreferenceHelper
|
||||||
import com.github.libretube.obj.ShareData
|
import com.github.libretube.obj.ShareData
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import okhttp3.HttpUrl
|
||||||
|
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||||
|
|
||||||
class ShareDialog : DialogFragment() {
|
class ShareDialog : DialogFragment() {
|
||||||
private lateinit var id: String
|
private lateinit var id: String
|
||||||
@ -34,72 +38,76 @@ class ShareDialog : DialogFragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
var shareOptions = arrayOf(
|
val customInstanceUrl = getCustomInstanceFrontendUrl().toHttpUrlOrNull()
|
||||||
getString(R.string.piped),
|
|
||||||
getString(R.string.youtube)
|
|
||||||
)
|
|
||||||
val instanceUrl = getCustomInstanceFrontendUrl()
|
|
||||||
val shareableTitle = shareData.currentChannel
|
val shareableTitle = shareData.currentChannel
|
||||||
?: shareData.currentVideo
|
?: shareData.currentVideo
|
||||||
?: shareData.currentPlaylist.orEmpty()
|
?: shareData.currentPlaylist.orEmpty()
|
||||||
// add instanceUrl option if custom instance frontend url available
|
|
||||||
if (instanceUrl.isNotEmpty()) {
|
|
||||||
shareOptions += getString(R.string.instance)
|
|
||||||
}
|
|
||||||
|
|
||||||
val binding = DialogShareBinding.inflate(layoutInflater)
|
val binding = DialogShareBinding.inflate(layoutInflater)
|
||||||
|
|
||||||
return MaterialAlertDialogBuilder(requireContext())
|
binding.shareHostGroup.check(
|
||||||
.setTitle(getString(R.string.share))
|
when (PreferenceHelper.getInt(PreferenceKeys.SELECTED_SHARE_HOST, 0)) {
|
||||||
.setItems(shareOptions) { _, which ->
|
0 -> binding.youtube.id
|
||||||
val host = when (which) {
|
1 -> binding.piped.id
|
||||||
0 -> PIPED_FRONTEND_URL
|
else -> if (customInstanceUrl != null) binding.customInstance.id else 0
|
||||||
1 -> YOUTUBE_FRONTEND_URL
|
|
||||||
// only available for custom instances
|
|
||||||
else -> instanceUrl
|
|
||||||
}
|
}
|
||||||
var url = when {
|
)
|
||||||
shareObjectType == ShareObjectType.VIDEO && host == YOUTUBE_FRONTEND_URL -> "$YOUTUBE_SHORT_URL/$id"
|
|
||||||
shareObjectType == ShareObjectType.VIDEO -> "$host/watch?v=$id"
|
binding.shareHostGroup.setOnCheckedChangeListener { _, _ ->
|
||||||
shareObjectType == ShareObjectType.PLAYLIST -> "$host/playlist?list=$id"
|
binding.linkPreview.text = generateLinkText(binding, customInstanceUrl)
|
||||||
else -> "$host/channel/$id"
|
PreferenceHelper.putInt(
|
||||||
|
PreferenceKeys.SELECTED_SHARE_HOST, when {
|
||||||
|
binding.youtube.isChecked -> 0
|
||||||
|
binding.piped.isChecked -> 1
|
||||||
|
else -> 2
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shareObjectType == ShareObjectType.VIDEO && binding.timeCodeSwitch.isChecked) {
|
if (customInstanceUrl != null) {
|
||||||
url += "&t=${binding.timeStamp.text}"
|
binding.customInstance.isVisible = true
|
||||||
|
binding.customInstance.text = customInstanceUrl.host
|
||||||
}
|
}
|
||||||
|
|
||||||
val intent = Intent(Intent.ACTION_SEND)
|
|
||||||
.putExtra(Intent.EXTRA_TEXT, url)
|
|
||||||
.putExtra(Intent.EXTRA_SUBJECT, shareableTitle)
|
|
||||||
.setType("text/plain")
|
|
||||||
val shareIntent = Intent.createChooser(intent, getString(R.string.shareTo))
|
|
||||||
requireContext().startActivity(shareIntent)
|
|
||||||
}
|
|
||||||
.apply {
|
|
||||||
if (shareObjectType == ShareObjectType.VIDEO) {
|
if (shareObjectType == ShareObjectType.VIDEO) {
|
||||||
setupTimeStampBinding(binding)
|
binding.timeStampSwitchLayout.isVisible = true
|
||||||
setView(binding.root)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun setupTimeStampBinding(binding: DialogShareBinding) {
|
|
||||||
binding.timeCodeSwitch.isChecked = PreferenceHelper.getBoolean(
|
binding.timeCodeSwitch.isChecked = PreferenceHelper.getBoolean(
|
||||||
PreferenceKeys.SHARE_WITH_TIME_CODE,
|
PreferenceKeys.SHARE_WITH_TIME_CODE,
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
binding.timeCodeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
binding.timeCodeSwitch.setOnCheckedChangeListener { _, isChecked ->
|
||||||
binding.timeStampLayout.isVisible = isChecked
|
binding.timeStampInputLayout.isVisible = isChecked
|
||||||
PreferenceHelper.putBoolean(PreferenceKeys.SHARE_WITH_TIME_CODE, isChecked)
|
PreferenceHelper.putBoolean(PreferenceKeys.SHARE_WITH_TIME_CODE, isChecked)
|
||||||
|
binding.linkPreview.text = generateLinkText(binding, customInstanceUrl)
|
||||||
|
}
|
||||||
|
binding.timeStamp.addTextChangedListener {
|
||||||
|
binding.linkPreview.text = generateLinkText(binding, customInstanceUrl)
|
||||||
}
|
}
|
||||||
binding.timeStamp.setText((shareData.currentPosition ?: 0L).toString())
|
binding.timeStamp.setText((shareData.currentPosition ?: 0L).toString())
|
||||||
if (binding.timeCodeSwitch.isChecked) {
|
if (binding.timeCodeSwitch.isChecked) {
|
||||||
binding.timeStampLayout.isVisible = true
|
binding.timeStampInputLayout.isVisible = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
binding.copyLink.setOnClickListener {
|
||||||
|
ClipboardHelper.save(requireContext(), text = binding.linkPreview.text.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.linkPreview.text = generateLinkText(binding, customInstanceUrl)
|
||||||
|
|
||||||
|
return MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(getString(R.string.share))
|
||||||
|
.setView(binding.root)
|
||||||
|
.setPositiveButton(R.string.share) { _, _ ->
|
||||||
|
val intent = Intent(Intent.ACTION_SEND)
|
||||||
|
.putExtra(Intent.EXTRA_TEXT, binding.linkPreview.text)
|
||||||
|
.putExtra(Intent.EXTRA_SUBJECT, shareableTitle)
|
||||||
|
.setType("text/plain")
|
||||||
|
val shareIntent = Intent.createChooser(intent, getString(R.string.shareTo))
|
||||||
|
requireContext().startActivity(shareIntent)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
// get the frontend url if it's a custom instance
|
// get the frontend url if it's a custom instance
|
||||||
private fun getCustomInstanceFrontendUrl(): String {
|
private fun getCustomInstanceFrontendUrl(): String {
|
||||||
val instancePref = PreferenceHelper.getString(
|
val instancePref = PreferenceHelper.getString(
|
||||||
@ -116,6 +124,27 @@ class ShareDialog : DialogFragment() {
|
|||||||
return customInstances.firstOrNull { it.apiUrl == instancePref }?.frontendUrl.orEmpty()
|
return customInstances.firstOrNull { it.apiUrl == instancePref }?.frontendUrl.orEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun generateLinkText(binding: DialogShareBinding, customInstanceUrl: HttpUrl?): String {
|
||||||
|
val host = when {
|
||||||
|
binding.piped.isChecked -> PIPED_FRONTEND_URL
|
||||||
|
binding.youtube.isChecked -> YOUTUBE_FRONTEND_URL
|
||||||
|
// only available for custom instances
|
||||||
|
else -> customInstanceUrl!!.toString()
|
||||||
|
}
|
||||||
|
var url = when {
|
||||||
|
shareObjectType == ShareObjectType.VIDEO && host == YOUTUBE_FRONTEND_URL -> "$YOUTUBE_SHORT_URL/$id"
|
||||||
|
shareObjectType == ShareObjectType.VIDEO -> "$host/watch?v=$id"
|
||||||
|
shareObjectType == ShareObjectType.PLAYLIST -> "$host/playlist?list=$id"
|
||||||
|
else -> "$host/channel/$id"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shareObjectType == ShareObjectType.VIDEO && binding.timeCodeSwitch.isChecked) {
|
||||||
|
url += "&t=${binding.timeStamp.text}"
|
||||||
|
}
|
||||||
|
|
||||||
|
return url
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val YOUTUBE_FRONTEND_URL = "https://www.youtube.com"
|
const val YOUTUBE_FRONTEND_URL = "https://www.youtube.com"
|
||||||
const val YOUTUBE_SHORT_URL = "https://youtu.be"
|
const val YOUTUBE_SHORT_URL = "https://youtu.be"
|
||||||
|
@ -4,15 +4,51 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingBottom="25dp">
|
android:paddingHorizontal="20dp"
|
||||||
|
android:paddingTop="10dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="25dp"
|
android:orientation="vertical"
|
||||||
android:orientation="horizontal"
|
|
||||||
tools:ignore="UselessParent">
|
tools:ignore="UselessParent">
|
||||||
|
|
||||||
|
<RadioGroup
|
||||||
|
android:id="@+id/share_host_group"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/youtube"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/youtube" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/piped"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/piped" />
|
||||||
|
|
||||||
|
<RadioButton
|
||||||
|
android:id="@+id/custom_instance"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:text="kavin.rocks"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</RadioGroup>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/time_stamp_switch_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -29,10 +65,9 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.google.android.material.textfield.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/timeStampLayout"
|
android:id="@+id/time_stamp_input_layout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginHorizontal="25dp"
|
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:hint="@string/time_code"
|
android:hint="@string/time_code"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
@ -45,4 +80,38 @@
|
|||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
android:id="@+id/share_link_card"
|
||||||
|
style="@style/Widget.Material3.CardView.Elevated"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_marginTop="10dp"
|
||||||
|
android:background="?selectableItemBackground"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/copy_link"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginHorizontal="12dp"
|
||||||
|
android:layout_marginVertical="16dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/link_preview"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="1"
|
||||||
|
tools:text="https://youtu.be/abcdefghijgk" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:src="@drawable/ic_copy" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
Loading…
Reference in New Issue
Block a user