add and remove playlist

This commit is contained in:
rimthekid 2022-04-17 11:50:10 -07:00
parent 42cbb58c15
commit 87466063d8
13 changed files with 296 additions and 91 deletions

View File

@ -0,0 +1,109 @@
package com.github.libretube
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.github.libretube.adapters.PlaylistsAdapter
import com.github.libretube.obj.PlaylistId
import retrofit2.HttpException
import java.io.IOException
class AddtoPlaylistDialog : DialogFragment() {
private val TAG = "AddToPlaylistDialog"
private lateinit var videoId: String
private lateinit var token: String
private lateinit var spinner: Spinner
private lateinit var button: Button
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
videoId = arguments?.getString("videoId")!!
val builder = AlertDialog.Builder(it)
// Get the layout inflater
val inflater = requireActivity().layoutInflater;
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
token = sharedPref?.getString("token","")!!
var view: View = inflater.inflate(R.layout.dialog_addtoplaylist, null)
spinner = view.findViewById(R.id.playlists_spinner)
button = view.findViewById(R.id.addToPlaylist)
if(token!=""){
fetchPlaylists()
}
builder.setView(view)
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
private fun fetchPlaylists(){
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.playlists(token)
}catch(e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
}
if (response.isNotEmpty()){
var names = emptyList<String>().toMutableList()
for(playlist in response){
names.add(playlist.name!!)
}
val arrayAdapter = ArrayAdapter(requireContext(),android.R.layout.simple_spinner_item,names)
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner.adapter = arrayAdapter
runOnUiThread {
button.setOnClickListener {
addToPlaylist(response[spinner.selectedItemPosition].id!!)
}
}
}else{
}
}
}
run()
}
private fun addToPlaylist(playlistId: String){
fun run() {
lifecycleScope.launchWhenCreated {
val response = try {
RetrofitInstance.api.addToPlaylist(token, PlaylistId(playlistId, videoId))
}catch(e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
Toast.makeText(context,R.string.unknown_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
Toast.makeText(context,R.string.server_error, Toast.LENGTH_SHORT).show()
return@launchWhenCreated
}
if (response.message == "ok"){
Toast.makeText(context,R.string.success, Toast.LENGTH_SHORT).show()
dialog?.dismiss()
}else{
Toast.makeText(context,R.string.fail, Toast.LENGTH_SHORT).show()
}
}
}
run()
}
private fun Fragment?.runOnUiThread(action: () -> Unit) {
this ?: return
if (!isAdded) return // Fragment not attached to an Activity
activity?.runOnUiThread(action)
}
}

View File

@ -1,44 +0,0 @@
package com.github.libretube
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
import com.github.libretube.obj.Login
class CreatePlaylistDialog : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
// Get the layout inflater
val inflater = requireActivity().layoutInflater;
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
val token = sharedPref?.getString("token","")
var view: View = inflater.inflate(R.layout.dialog_createplaylist, null)
Log.e("dafaq",token!!)
if(token!=""){
val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE)
val user = sharedPref2?.getString("username","")
view.findViewById<TextView>(R.id.user).text = view.findViewById<TextView>(R.id.user).text.toString()+" ("+user+")"
view.findViewById<Button>(R.id.logout).setOnClickListener {
Toast.makeText(context,R.string.loggedout, Toast.LENGTH_SHORT).show()
val sharedPref = context?.getSharedPreferences("token", Context.MODE_PRIVATE)
with (sharedPref!!.edit()) {
putString("token","")
apply()
}
dialog?.dismiss()
}
dialog?.dismiss()
}
builder.setView(view)
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}

View File

@ -62,6 +62,7 @@ class Library : Fragment() {
val playlistName = view.findViewById<EditText>(R.id.playlists_name) val playlistName = view.findViewById<EditText>(R.id.playlists_name)
view.findViewById<Button>(R.id.create_playlist).setOnClickListener { view.findViewById<Button>(R.id.create_playlist).setOnClickListener {
if(playlistName.text.toString()!="") createPlaylist(playlistName.text.toString(),view) if(playlistName.text.toString()!="") createPlaylist(playlistName.text.toString(),view)
hideKeyboard()
} }
} else{ } else{
with(view.findViewById<ImageView>(R.id.boogh2)){ with(view.findViewById<ImageView>(R.id.boogh2)){
@ -102,7 +103,7 @@ class Library : Fragment() {
visibility=View.GONE visibility=View.GONE
} }
} }
val playlistsAdapter = PlaylistsAdapter(response.toMutableList()) val playlistsAdapter = PlaylistsAdapter(response.toMutableList(),requireActivity())
playlistRecyclerView.adapter= playlistsAdapter playlistRecyclerView.adapter= playlistsAdapter
}else{ }else{
runOnUiThread { runOnUiThread {
@ -153,4 +154,4 @@ class Library : Fragment() {
if (!isAdded) return // Fragment not attached to an Activity if (!isAdded) return // Fragment not attached to an Activity
activity?.runOnUiThread(action) activity?.runOnUiThread(action)
} }
} }

View File

@ -64,6 +64,12 @@ interface PipedApi {
@POST("user/playlists/create") @POST("user/playlists/create")
suspend fun createPlaylist(@Header("Authorization") token: String, @Body name: Playlists): PlaylistId suspend fun createPlaylist(@Header("Authorization") token: String, @Body name: Playlists): PlaylistId
@POST("user/playlists/add")
suspend fun addToPlaylist(@Header("Authorization") token: String, @Body playlistId: PlaylistId): Message
@POST("user/playlists/remove")
suspend fun removeFromPlaylist(@Header("Authorization") token: String, @Body playlistId: PlaylistId): Message
//only for fetching servers list //only for fetching servers list
@GET @GET
suspend fun getInstances(@Url url: String): List<Instances> suspend fun getInstances(@Url url: String): List<Instances>

View File

@ -502,6 +502,13 @@ class PlayerFragment : Fragment() {
val channelId = response.uploaderUrl?.replace("/channel/", "") val channelId = response.uploaderUrl?.replace("/channel/", "")
val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe) val subButton = view.findViewById<MaterialButton>(R.id.player_subscribe)
isSubscribed(subButton, channelId!!) isSubscribed(subButton, channelId!!)
view.findViewById<RelativeLayout>(R.id.save).setOnClickListener {
val newFragment = AddtoPlaylistDialog()
var bundle = Bundle()
bundle.putString("videoId", videoId)
newFragment.arguments = bundle
newFragment.show(childFragmentManager, "AddToPlaylist")
}
} }
// share button // share button
view.findViewById<RelativeLayout>(R.id.relPlayer_share).setOnClickListener { view.findViewById<RelativeLayout>(R.id.relPlayer_share).setOnClickListener {

View File

@ -1,5 +1,6 @@
package com.github.libretube package com.github.libretube
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -69,7 +70,13 @@ class PlaylistFragment : Fragment() {
view.findViewById<TextView>(R.id.playlist_name).text=response.name view.findViewById<TextView>(R.id.playlist_name).text=response.name
view.findViewById<TextView>(R.id.playlist_uploader).text=response.uploader view.findViewById<TextView>(R.id.playlist_uploader).text=response.uploader
view.findViewById<TextView>(R.id.playlist_totVideos).text=response.videos.toString()+" Videos" view.findViewById<TextView>(R.id.playlist_totVideos).text=response.videos.toString()+" Videos"
playlistAdapter = PlaylistAdapter(response.relatedStreams!!.toMutableList()) val sharedPref2 = context?.getSharedPreferences("username", Context.MODE_PRIVATE)
val user = sharedPref2?.getString("username","")
var isOwner = false
if(response.uploaderUrl == null && response.uploader == user){
isOwner = true
}
playlistAdapter = PlaylistAdapter(response.relatedStreams!!.toMutableList(), playlist_id!!, isOwner, requireActivity())
view.findViewById<RecyclerView>(R.id.playlist_recView).adapter = playlistAdapter view.findViewById<RecyclerView>(R.id.playlist_recView).adapter = playlistAdapter
val scrollView = view.findViewById<ScrollView>(R.id.playlist_scrollview) val scrollView = view.findViewById<ScrollView>(R.id.playlist_scrollview)
scrollView.viewTreeObserver scrollView.viewTreeObserver

View File

@ -1,7 +1,10 @@
package com.github.libretube.adapters package com.github.libretube.adapters
import android.app.Activity
import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.text.format.DateUtils import android.text.format.DateUtils
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -12,9 +15,16 @@ import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso import com.squareup.picasso.Picasso
import com.github.libretube.PlayerFragment import com.github.libretube.PlayerFragment
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.RetrofitInstance
import com.github.libretube.obj.PlaylistId
import com.github.libretube.obj.StreamItem import com.github.libretube.obj.StreamItem
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import retrofit2.HttpException
import java.io.IOException
class PlaylistAdapter(private val videoFeed: MutableList<StreamItem>): RecyclerView.Adapter<PlaylistViewHolder>() { class PlaylistAdapter(private val videoFeed: MutableList<StreamItem>, private val playlistId: String, private val isOwner: Boolean, private val activity: Activity): RecyclerView.Adapter<PlaylistViewHolder>() {
private val TAG = "PlaylistAdapter"
override fun getItemCount(): Int { override fun getItemCount(): Int {
return videoFeed.size return videoFeed.size
} }
@ -25,16 +35,16 @@ class PlaylistAdapter(private val videoFeed: MutableList<StreamItem>): RecyclerV
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PlaylistViewHolder {
val layoutInflater = LayoutInflater.from(parent.context) val layoutInflater = LayoutInflater.from(parent.context)
val cell = layoutInflater.inflate(R.layout.video_channel_row,parent,false) val cell = layoutInflater.inflate(R.layout.playlist_row,parent,false)
return PlaylistViewHolder(cell) return PlaylistViewHolder(cell)
} }
override fun onBindViewHolder(holder: PlaylistViewHolder, position: Int) { override fun onBindViewHolder(holder: PlaylistViewHolder, position: Int) {
val streamItem = videoFeed[position] val streamItem = videoFeed[position]
holder.v.findViewById<TextView>(R.id.channel_description).text = streamItem.title holder.v.findViewById<TextView>(R.id.playlist_title).text = streamItem.title
holder.v.findViewById<TextView>(R.id.channel_views).text = streamItem.uploaderName holder.v.findViewById<TextView>(R.id.playlist_description).text = streamItem.uploaderName
holder.v.findViewById<TextView>(R.id.channel_duration).text = DateUtils.formatElapsedTime(streamItem.duration!!) holder.v.findViewById<TextView>(R.id.playlist_duration).text = DateUtils.formatElapsedTime(streamItem.duration!!)
val thumbnailImage = holder.v.findViewById<ImageView>(R.id.channel_thumbnail) val thumbnailImage = holder.v.findViewById<ImageView>(R.id.playlist_thumbnail)
Picasso.get().load(streamItem.thumbnail).into(thumbnailImage) Picasso.get().load(streamItem.thumbnail).into(thumbnailImage)
holder.v.setOnClickListener{ holder.v.setOnClickListener{
var bundle = Bundle() var bundle = Bundle()
@ -49,6 +59,50 @@ class PlaylistAdapter(private val videoFeed: MutableList<StreamItem>): RecyclerV
.replace(R.id.container, frag) .replace(R.id.container, frag)
.commitNow() .commitNow()
} }
if(isOwner){
val delete = holder.v.findViewById<ImageView>(R.id.delete_playlist)
delete.visibility = View.VISIBLE
delete.setOnClickListener {
val sharedPref = holder.v.context.getSharedPreferences("token", Context.MODE_PRIVATE)
val token = sharedPref?.getString("token","")!!
removeFromPlaylist(token, position)
}
}
}
private fun removeFromPlaylist(token: String, position: Int) {
fun run() {
GlobalScope.launch{
val response = try {
RetrofitInstance.api.removeFromPlaylist(token, PlaylistId(playlistId = playlistId, index = position))
}catch(e: IOException) {
println(e)
Log.e(TAG, "IOException, you might not have internet connection")
return@launch
} catch (e: HttpException) {
Log.e(TAG, "HttpException, unexpected response")
return@launch
}finally {
}
try{
if(response.message == "ok"){
Log.d(TAG,"deleted!")
videoFeed.removeAt(position)
// FIXME: This needs to run on UI thread?
activity.runOnUiThread { notifyDataSetChanged() }
/*if(playlists.isEmpty()){
view.findViewById<ImageView>(R.id.boogh2).visibility=View.VISIBLE
}*/
}
}catch (e:Exception){
Log.e(TAG,e.toString())
}
}
}
run()
} }
} }
class PlaylistViewHolder(val v: View): RecyclerView.ViewHolder(v){ class PlaylistViewHolder(val v: View): RecyclerView.ViewHolder(v){

View File

@ -1,35 +1,27 @@
package com.github.libretube.adapters package com.github.libretube.adapters
import android.app.Activity
import android.content.Context import android.content.Context
import android.media.Image import android.os.Handler
import android.os.Bundle
import android.text.format.DateUtils
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.*
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.* import com.github.libretube.*
import com.github.libretube.obj.PlaylistId import com.github.libretube.obj.PlaylistId
import com.squareup.picasso.Picasso
import com.github.libretube.obj.StreamItem
import com.github.libretube.obj.Playlists import com.github.libretube.obj.Playlists
import com.squareup.picasso.Picasso
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException import java.io.IOException
class PlaylistsAdapter(private val playlists: MutableList<Playlists>): RecyclerView.Adapter<PlaylistsViewHolder>() {
class PlaylistsAdapter(private val playlists: MutableList<Playlists>, private val activity: Activity): RecyclerView.Adapter<PlaylistsViewHolder>() {
val TAG = "PlaylistsAdapter" val TAG = "PlaylistsAdapter"
override fun getItemCount(): Int { override fun getItemCount(): Int {
return playlists.size return playlists.size
@ -91,7 +83,8 @@ class PlaylistsAdapter(private val playlists: MutableList<Playlists>): RecyclerV
Log.d(TAG,"deleted!") Log.d(TAG,"deleted!")
playlists.removeAt(position) playlists.removeAt(position)
// FIXME: This needs to run on UI thread? // FIXME: This needs to run on UI thread?
notifyDataSetChanged() activity.runOnUiThread { notifyDataSetChanged() }
/*if(playlists.isEmpty()){ /*if(playlists.isEmpty()){
view.findViewById<ImageView>(R.id.boogh2).visibility=View.VISIBLE view.findViewById<ImageView>(R.id.boogh2).visibility=View.VISIBLE
}*/ }*/
@ -105,6 +98,7 @@ class PlaylistsAdapter(private val playlists: MutableList<Playlists>): RecyclerV
run() run()
} }
} }
class PlaylistsViewHolder(val v: View): RecyclerView.ViewHolder(v){ class PlaylistsViewHolder(val v: View): RecyclerView.ViewHolder(v){
init { init {

View File

@ -6,4 +6,5 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
data class PlaylistId( data class PlaylistId(
var playlistId: String? = null, var playlistId: String? = null,
var videoId: String? = null, var videoId: String? = null,
var index: Int = -1,
) )

View File

@ -14,36 +14,22 @@
android:scaleType="center" android:scaleType="center"
android:background="#CD5757" android:background="#CD5757"
android:contentDescription="@string/app_name" /> android:contentDescription="@string/app_name" />
<Spinner
<com.google.android.material.textfield.TextInputLayout android:id="@+id/playlists_spinner"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintEnabled="false"
android:layout_marginTop="16dp"
android:layout_marginLeft="7dp"
android:layout_marginRight="7dp"
android:layout_marginBottom="4dp"
>
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/playlistName" android:padding="8dp"
android:inputType="text" android:layout_margin="8dp"/>
android:padding="12dp" />
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="horizontal" android:orientation="horizontal"
android:gravity="center"> android:gravity="center">
<Button <Button
android:id="@+id/create_playlist_button" android:id="@+id/addToPlaylist"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/createPlaylist" android:text="@string/addToPlaylist"
android:padding="8dp" android:padding="8dp"
android:layout_margin="8dp"/> android:layout_margin="8dp"/>

View File

@ -49,7 +49,7 @@
android:padding="8dp" android:padding="8dp"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:text="Title" android:text="Loading..."
android:textSize="18sp" android:textSize="18sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/player_description_arrow" app:layout_constraintEnd_toStartOf="@id/player_description_arrow"
@ -62,7 +62,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="8dp" android:paddingLeft="8dp"
android:paddingRight="8dp" android:paddingRight="8dp"
android:text="TextView" android:text=""
android:textSize="12sp" android:textSize="12sp"
app:layout_constraintEnd_toStartOf="@id/player_description_arrow" app:layout_constraintEnd_toStartOf="@id/player_description_arrow"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -192,9 +192,11 @@
android:text="@string/vlc" /> android:text="@string/vlc" />
</RelativeLayout> </RelativeLayout>
<RelativeLayout <RelativeLayout
android:id="@+id/save"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="1"> android:layout_weight="1"
android:background="?android:attr/selectableItemBackground">
<ImageView <ImageView
android:id="@+id/player_save" android:id="@+id/player_save"

View File

@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_marginBottom="16dp"
android:background="?android:attr/selectableItemBackground"
android:id="@+id/video_search"
>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent=".5"/>
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_search_thumbnail"
app:cardCornerRadius="8dp"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintDimensionRatio="16:9"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintEnd_toStartOf="@+id/guideline">
<ImageView
android:id="@+id/playlist_thumbnail"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:srcCompat="@tools:sample/backgrounds/scenic" />
<TextView
android:id="@+id/playlist_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="5dp"
android:textColor="@color/duration_text_color"
android:background="@color/duration_background_color"
android:padding="0.5dp"/>
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/playlist_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextView"
app:layout_constraintEnd_toStartOf="@+id/delete_playlist"
app:layout_constraintStart_toEndOf="@+id/card_search_thumbnail"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/playlist_description"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextView"
app:layout_constraintEnd_toStartOf="@+id/delete_playlist"
app:layout_constraintStart_toEndOf="@+id/card_search_thumbnail"
app:layout_constraintTop_toBottomOf="@+id/playlist_title" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/delete_playlist"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:shapeAppearanceOverlay="@style/roundedImageViewRounded"
android:src="@drawable/ic_delete"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="?android:attr/selectableItemBackground"
android:padding="8dp"
android:visibility="gone"
/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -46,4 +46,7 @@
<string name="createPlaylist">Create Playlist</string> <string name="createPlaylist">Create Playlist</string>
<string name="playlistCreated">Playlist created!</string> <string name="playlistCreated">Playlist created!</string>
<string name="playlistName">Playlist Name</string> <string name="playlistName">Playlist Name</string>
<string name="addToPlaylist">Add to Playlist</string>
<string name="success">Success!</string>
<string name="fail">Failed :(</string>
</resources> </resources>