From 5a3eb6482aea75b21bba2688f8c97ee298e3ea3d Mon Sep 17 00:00:00 2001 From: Nite Date: Fri, 8 Oct 2021 17:34:52 +0200 Subject: [PATCH 1/2] Added option whether to create a share on the server when sharing songs --- .../moire/ultrasonic/util/ShareDetails.java | 1 + .../ultrasonic/fragment/PlayerFragment.kt | 94 ++++++++++-------- .../moire/ultrasonic/subsonic/ShareHandler.kt | 96 +++++++++++++++++-- .../org/moire/ultrasonic/util/Constants.kt | 1 + .../org/moire/ultrasonic/util/Settings.kt | 15 +++ .../src/main/res/layout/share_details.xml | 8 ++ ultrasonic/src/main/res/menu/nowplaying.xml | 5 + ultrasonic/src/main/res/values/strings.xml | 5 + ultrasonic/src/main/res/xml/settings.xml | 5 + 9 files changed, 180 insertions(+), 50 deletions(-) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShareDetails.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShareDetails.java index 91d4986d..0cffa6f8 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShareDetails.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/ShareDetails.java @@ -10,6 +10,7 @@ import java.util.List; public class ShareDetails { public String Description; + public boolean ShareOnServer; public long Expiration; public List Entries; } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt index f0ce17ba..b02372a4 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/PlayerFragment.kt @@ -33,6 +33,7 @@ import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener import android.widget.TextView import android.widget.ViewFlipper +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.navigation.Navigation import com.mobeta.android.dslv.DragSortListView @@ -153,7 +154,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon return inflater.inflate(R.layout.current_playing, container, false) } - fun findViews(view: View) { + private fun findViews(view: View) { playlistFlipper = view.findViewById(R.id.current_playing_playlist_flipper) emptyTextView = view.findViewById(R.id.playlist_empty) songTitleTextView = view.findViewById(R.id.current_playing_song) @@ -209,7 +210,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon val nextButton: AutoRepeatButton = view.findViewById(R.id.button_next) val shuffleButton = view.findViewById(R.id.button_shuffle) val ratingLinearLayout = view.findViewById(R.id.song_rating) - if (!useFiveStarRating) ratingLinearLayout.visibility = View.GONE + if (!useFiveStarRating) ratingLinearLayout.isVisible = false hollowStar = Util.getDrawableFromAttribute(view.context, R.attr.star_hollow) fullStar = Util.getDrawableFromAttribute(context, R.attr.star_full) @@ -375,7 +376,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon mediaPlayerController.isShufflePlayEnabled = true } - visualizerViewLayout.visibility = View.GONE + visualizerViewLayout.isVisible = false VisualizerController.get().observe( requireActivity(), { visualizerController -> @@ -389,11 +390,9 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon LinearLayout.LayoutParams.MATCH_PARENT ) ) - if (!visualizerView.isActive) { - visualizerViewLayout.visibility = View.GONE - } else { - visualizerViewLayout.visibility = View.VISIBLE - } + + visualizerViewLayout.isVisible = visualizerView.isActive + visualizerView.setOnTouchListener { _, _ -> visualizerView.isActive = !visualizerView.isActive mediaPlayerController.showVisualization = visualizerView.isActive @@ -402,7 +401,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon isVisualizerAvailable = true } else { Timber.d("VisualizerController Observer.onChanged has no controller") - visualizerViewLayout.visibility = View.GONE + visualizerViewLayout.isVisible = false isVisualizerAvailable = false } } @@ -497,6 +496,7 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon val equalizerMenuItem = menu.findItem(R.id.menu_item_equalizer) val visualizerMenuItem = menu.findItem(R.id.menu_item_visualizer) val shareMenuItem = menu.findItem(R.id.menu_item_share) + val shareSongMenuItem = menu.findItem(R.id.menu_item_share_song) starMenuItem = menu.findItem(R.id.menu_item_star) val bookmarkMenuItem = menu.findItem(R.id.menu_item_bookmark_set) val bookmarkRemoveMenuItem = menu.findItem(R.id.menu_item_bookmark_delete) @@ -523,20 +523,27 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon } val mediaPlayerController = mediaPlayerController val downloadFile = mediaPlayerController.currentPlaying + if (downloadFile != null) { currentSong = downloadFile.song } + if (useFiveStarRating) starMenuItem.isVisible = false + if (currentSong != null) { starMenuItem.icon = if (currentSong!!.starred) fullStar else hollowStar + shareSongMenuItem.isVisible = true } else { starMenuItem.icon = hollowStar + shareSongMenuItem.isVisible = false } + if (mediaPlayerController.keepScreenOn) { screenOption?.setTitle(R.string.download_menu_screen_off) } else { screenOption?.setTitle(R.string.download_menu_screen_on) } + if (jukeboxOption != null) { jukeboxOption.isEnabled = jukeboxAvailable jukeboxOption.isVisible = jukeboxAvailable @@ -598,9 +605,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon when (menuItemId) { R.id.menu_show_artist -> { - if (entry == null) { - return false - } + if (entry == null) return false + if (Settings.shouldUseId3Tags) { bundle = Bundle() bundle.putString(Constants.INTENT_EXTRA_NAME_ID, entry.artistId) @@ -613,9 +619,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon return true } R.id.menu_show_album -> { - if (entry == null) { - return false - } + if (entry == null) return false + val albumId = if (Settings.shouldUseId3Tags) entry.albumId else entry.parent bundle = Bundle() bundle.putString(Constants.INTENT_EXTRA_NAME_ID, albumId) @@ -627,9 +632,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon return true } R.id.menu_lyrics -> { - if (entry == null) { - return false - } + if (entry == null) return false + bundle = Bundle() bundle.putString(Constants.INTENT_EXTRA_NAME_ARTIST, entry.artist) bundle.putString(Constants.INTENT_EXTRA_NAME_TITLE, entry.title) @@ -664,11 +668,9 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon R.id.menu_item_visualizer -> { val active = !visualizerView.isActive visualizerView.isActive = active - if (!visualizerView.isActive) { - visualizerViewLayout.visibility = View.GONE - } else { - visualizerViewLayout.visibility = View.VISIBLE - } + + visualizerViewLayout.isVisible = visualizerView.isActive + mediaPlayerController.showVisualization = visualizerView.isActive Util.toast( context, @@ -705,9 +707,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon return true } R.id.menu_item_star -> { - if (currentSong == null) { - return true - } + if (currentSong == null) return true + val isStarred = currentSong!!.starred val id = currentSong!!.id if (isStarred) { @@ -732,9 +733,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon return true } R.id.menu_item_bookmark_set -> { - if (currentSong == null) { - return true - } + if (currentSong == null) return true + val songId = currentSong!!.id val playerPosition = mediaPlayerController.playerPosition currentSong!!.bookmarkPosition = playerPosition @@ -755,9 +755,8 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon return true } R.id.menu_item_bookmark_delete -> { - if (currentSong == null) { - return true - } + if (currentSong == null) return true + val bookmarkSongId = currentSong!!.id currentSong!!.bookmarkPosition = 0 Thread { @@ -782,6 +781,15 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon shareHandler.createShare(this, entries, null, cancellationToken) return true } + R.id.menu_item_share_song -> { + if (currentSong == null) return true + + val entries: MutableList = ArrayList() + entries.add(currentSong) + + shareHandler.createShare(this, entries, null, cancellationToken) + return true + } else -> return false } } @@ -903,7 +911,9 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon onCurrentChanged() } }) - emptyTextView.visibility = if (list.isEmpty()) View.VISIBLE else View.GONE + + emptyTextView.isVisible = list.isEmpty() + currentRevision = mediaPlayerController.playListUpdateRevision when (mediaPlayerController.repeatMode) { RepeatMode.OFF -> repeatButton.setImageDrawable( @@ -1028,19 +1038,19 @@ class PlayerFragment : Fragment(), GestureDetector.OnGestureListener, KoinCompon when (playerState) { PlayerState.STARTED -> { - pauseButton.visibility = View.VISIBLE - stopButton.visibility = View.GONE - startButton.visibility = View.GONE + pauseButton.isVisible = true + stopButton.isVisible = false + startButton.isVisible = false } PlayerState.DOWNLOADING, PlayerState.PREPARING -> { - pauseButton.visibility = View.GONE - stopButton.visibility = View.VISIBLE - startButton.visibility = View.GONE + pauseButton.isVisible = false + stopButton.isVisible = true + startButton.isVisible = false } else -> { - pauseButton.visibility = View.GONE - stopButton.visibility = View.GONE - startButton.visibility = View.VISIBLE + pauseButton.isVisible = false + stopButton.isVisible = false + startButton.isVisible = true } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt index 57d70acf..883aeeac 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ShareHandler.kt @@ -7,6 +7,8 @@ import android.view.LayoutInflater import android.view.View import android.widget.CheckBox import android.widget.EditText +import android.widget.TextView +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import java.util.Locale @@ -31,9 +33,12 @@ import org.moire.ultrasonic.util.TimeSpanPicker class ShareHandler(val context: Context) { private var shareDescription: EditText? = null private var timeSpanPicker: TimeSpanPicker? = null + private var shareOnServerCheckBox: CheckBox? = null private var hideDialogCheckBox: CheckBox? = null private var noExpirationCheckBox: CheckBox? = null private var saveAsDefaultsCheckBox: CheckBox? = null + private var textViewComment: TextView? = null + private var textViewExpiration: TextView? = null private val pattern = Pattern.compile(":") fun createShare( @@ -62,15 +67,17 @@ class ShareHandler(val context: Context) { swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken ) { - val task: BackgroundTask = object : FragmentBackgroundTask( + val task: BackgroundTask = object : FragmentBackgroundTask( fragment.requireActivity(), true, swipe, cancellationToken ) { @Throws(Throwable::class) - override fun doInBackground(): Share { + override fun doInBackground(): Share? { val ids: MutableList = ArrayList() + + if (!shareDetails.ShareOnServer && shareDetails.Entries.size == 1) return null if (shareDetails.Entries.isEmpty()) { fragment.arguments?.getString(Constants.INTENT_EXTRA_NAME_ID)?.let { ids.add(it) @@ -80,23 +87,51 @@ class ShareHandler(val context: Context) { ids.add(id) } } + val musicService = getMusicService() var timeInMillis: Long = 0 + if (shareDetails.Expiration != 0L) { timeInMillis = shareDetails.Expiration } + val shares = musicService.createShare(ids, shareDetails.Description, timeInMillis) + return shares[0] } - override fun done(result: Share) { + override fun done(result: Share?) { + val intent = Intent(Intent.ACTION_SEND) intent.type = "text/plain" - intent.putExtra( - Intent.EXTRA_TEXT, - String.format(Locale.ROOT, "%s\n\n%s", Settings.shareGreeting, result.url) - ) + + if (result != null) { + // Created a share, send the URL + intent.putExtra( + Intent.EXTRA_TEXT, + String.format( + Locale.ROOT, "%s\n\n%s", Settings.shareGreeting, result.url + ) + ) + } else { + // Sending only text details + val textBuilder = StringBuilder() + textBuilder.appendLine(Settings.shareGreeting) + + if (!shareDetails.Entries[0].title.isNullOrEmpty()) + textBuilder.append(context.resources.getString(R.string.common_title)) + .append(": ").appendLine(shareDetails.Entries[0].title) + if (!shareDetails.Entries[0].artist.isNullOrEmpty()) + textBuilder.append(context.resources.getString(R.string.common_artist)) + .append(": ").appendLine(shareDetails.Entries[0].artist) + if (!shareDetails.Entries[0].album.isNullOrEmpty()) + textBuilder.append(context.resources.getString(R.string.common_album)) + .append(": ").append(shareDetails.Entries[0].album) + + intent.putExtra(Intent.EXTRA_TEXT, textBuilder.toString()) + } + fragment.activity?.startActivity( Intent.createChooser( intent, @@ -119,24 +154,45 @@ class ShareHandler(val context: Context) { if (layout != null) { shareDescription = layout.findViewById(R.id.share_description) as EditText hideDialogCheckBox = layout.findViewById(R.id.hide_dialog) as CheckBox + shareOnServerCheckBox = layout.findViewById(R.id.share_on_server) as CheckBox noExpirationCheckBox = layout.findViewById( R.id.timeSpanDisableCheckBox ) as CheckBox saveAsDefaultsCheckBox = layout.findViewById(R.id.save_as_defaults) as CheckBox timeSpanPicker = layout.findViewById(R.id.date_picker) as TimeSpanPicker + textViewComment = layout.findViewById(R.id.textViewComment) as TextView + textViewExpiration = layout.findViewById(R.id.textViewExpiration) as TextView } + + if (shareDetails.Entries.size == 1) { + // For single songs the sharing may be done by text only + shareOnServerCheckBox?.setOnCheckedChangeListener { _, _ -> + updateVisibility() + } + + shareOnServerCheckBox?.isChecked = Settings.shareOnServer + } else { + shareOnServerCheckBox?.isVisible = false + } + updateVisibility() + val builder = AlertDialog.Builder(fragment.context) builder.setTitle(R.string.share_set_share_options) - builder.setPositiveButton(R.string.common_save) { _, _ -> + + builder.setPositiveButton(R.string.menu_share) { _, _ -> if (!noExpirationCheckBox!!.isChecked) { val timeSpan: TimeSpan = timeSpanPicker!!.timeSpan val now = TimeSpan.getCurrentTime() shareDetails.Expiration = now.add(timeSpan).totalMilliseconds } + shareDetails.Description = shareDescription!!.text.toString() + shareDetails.ShareOnServer = shareOnServerCheckBox!!.isChecked + if (hideDialogCheckBox!!.isChecked) { Settings.shouldAskForShareDetails = false } + if (saveAsDefaultsCheckBox!!.isChecked) { val timeSpanType: String = timeSpanPicker!!.timeSpanType val timeSpanAmount: Int = timeSpanPicker!!.timeSpanAmount @@ -145,22 +201,29 @@ class ShareHandler(val context: Context) { String.format("%d:%s", timeSpanAmount, timeSpanType) else "" Settings.defaultShareDescription = shareDetails.Description + Settings.shareOnServer = shareDetails.ShareOnServer } + share(fragment, shareDetails, swipe, cancellationToken) } + builder.setNegativeButton(R.string.common_cancel) { dialog, _ -> dialog.cancel() } + builder.setView(layout) builder.setCancelable(true) + timeSpanPicker!!.setTimeSpanDisableText(context.resources.getString(R.string.no_expiration)) noExpirationCheckBox!!.setOnCheckedChangeListener { _, b -> timeSpanPicker!!.isEnabled = !b } + val defaultDescription = Settings.defaultShareDescription val timeSpan = Settings.defaultShareExpiration + val split = pattern.split(timeSpan) if (split.size == 2) { val timeSpanAmount = split[0].toInt() @@ -178,8 +241,25 @@ class ShareHandler(val context: Context) { noExpirationCheckBox!!.isChecked = true timeSpanPicker!!.isEnabled = false } + shareDescription!!.setText(defaultDescription) builder.create() builder.show() } + + private fun updateVisibility() { + if (!shareOnServerCheckBox!!.isVisible || shareOnServerCheckBox!!.isChecked) { + noExpirationCheckBox?.isVisible = true + timeSpanPicker?.isVisible = true + shareDescription?.isVisible = true + textViewComment?.isVisible = true + textViewExpiration?.isVisible = true + } else { + noExpirationCheckBox?.isVisible = false + timeSpanPicker?.isVisible = false + shareDescription?.isVisible = false + textViewComment?.isVisible = false + textViewExpiration?.isVisible = false + } + } } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Constants.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Constants.kt index 908974c4..6206378f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Constants.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Constants.kt @@ -105,6 +105,7 @@ object Constants { const val PREFERENCES_KEY_ASK_FOR_SHARE_DETAILS = "sharingAlwaysAskForDetails" const val PREFERENCES_KEY_DEFAULT_SHARE_DESCRIPTION = "sharingDefaultDescription" const val PREFERENCES_KEY_DEFAULT_SHARE_GREETING = "sharingDefaultGreeting" + const val PREFERENCES_KEY_SHARE_ON_SERVER = "sharingCreateOnServer" const val PREFERENCES_KEY_DEFAULT_SHARE_EXPIRATION = "sharingDefaultExpiration" const val PREFERENCES_KEY_SHOW_ALL_SONGS_BY_ARTIST = "showAllSongsByArtist" const val PREFERENCES_KEY_USE_FIVE_STAR_RATING = "use_five_star_rating" diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt index 24f97fa9..ed8d2847 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/Settings.kt @@ -376,6 +376,21 @@ object Settings { ) } + var shareOnServer: Boolean + get() { + val preferences = preferences + return preferences.getBoolean(Constants.PREFERENCES_KEY_SHARE_ON_SERVER, true)!! + } + set(shareOnServer) { + val preferences = preferences + val editor = preferences.edit() + editor.putBoolean( + Constants.PREFERENCES_KEY_SHARE_ON_SERVER, + shareOnServer + ) + editor.apply() + } + var defaultShareExpiration: String get() { val preferences = preferences diff --git a/ultrasonic/src/main/res/layout/share_details.xml b/ultrasonic/src/main/res/layout/share_details.xml index 6790f366..26d3e8bb 100644 --- a/ultrasonic/src/main/res/layout/share_details.xml +++ b/ultrasonic/src/main/res/layout/share_details.xml @@ -10,6 +10,14 @@ a:layout_width="wrap_content" a:layout_height="wrap_content"> + + + Playlists Search Send a message + Album Ultrasonic + Artist Cancel Comment Confirm @@ -48,6 +50,7 @@ Play Shuffled Public Save + Title Unpin Various Artists Do you want to delete %1$s @@ -355,6 +358,7 @@ Default Expiration Time Do not show dialog again Set Share Options + Create share on the server No Expiration Toggle Playlist Set Bookmark @@ -377,6 +381,7 @@ Time To Expiration \"%s\" was removed from playlist Share Playlist + Share Current Song Default Share Greeting Check out this music I shared from %s Share songs via diff --git a/ultrasonic/src/main/res/xml/settings.xml b/ultrasonic/src/main/res/xml/settings.xml index e1b672e5..8aa05da1 100644 --- a/ultrasonic/src/main/res/xml/settings.xml +++ b/ultrasonic/src/main/res/xml/settings.xml @@ -197,6 +197,11 @@ a:key="sharingDefaultGreeting" a:title="@string/settings.share_greeting_default" app:iconSpaceReserved="false"/> + Date: Tue, 12 Oct 2021 17:38:34 +0200 Subject: [PATCH 2/2] Updated Settings for sharing --- ultrasonic/src/main/res/values/strings.xml | 3 ++- ultrasonic/src/main/res/xml/settings.xml | 19 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/ultrasonic/src/main/res/values/strings.xml b/ultrasonic/src/main/res/values/strings.xml index b1b6b9b9..85b6d58c 100644 --- a/ultrasonic/src/main/res/values/strings.xml +++ b/ultrasonic/src/main/res/values/strings.xml @@ -353,12 +353,13 @@ No SD card Default Share Description Sharing - Always ask for description and expiration when creating a share + Always ask for description and expiration when creating a share on the server Always Ask For Details Default Expiration Time Do not show dialog again Set Share Options Create share on the server + Sharing will create a share on the server and share its URL. If disabled, only the song details are shared No Expiration Toggle Playlist Set Bookmark diff --git a/ultrasonic/src/main/res/xml/settings.xml b/ultrasonic/src/main/res/xml/settings.xml index 8aa05da1..09cd31cf 100644 --- a/ultrasonic/src/main/res/xml/settings.xml +++ b/ultrasonic/src/main/res/xml/settings.xml @@ -189,10 +189,6 @@ - - + +