diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 02c33d593..88aad0de5 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -12,7 +12,7 @@ - + diff --git a/.idea/misc.xml b/.idea/misc.xml index afbdd9bb4..e4b03fa03 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -9,15 +9,18 @@ + + + diff --git a/app/src/main/java/xyz/btcland/libretube/Home.kt b/app/src/main/java/xyz/btcland/libretube/Home.kt index a8a736feb..c54ab6f0e 100644 --- a/app/src/main/java/xyz/btcland/libretube/Home.kt +++ b/app/src/main/java/xyz/btcland/libretube/Home.kt @@ -13,8 +13,8 @@ import androidx.recyclerview.widget.RecyclerView import okhttp3.* import retrofit2.HttpException +import xyz.btcland.libretube.adapters.TrendingAdapter import java.io.IOException -import java.lang.Exception // TODO: Rename parameter arguments, choose names that match @@ -54,7 +54,6 @@ class Home : Fragment() { super.onViewCreated(view, savedInstanceState) val recyclerView = view.findViewById(R.id.recview) recyclerView.layoutManager = GridLayoutManager(view.context, resources.getInteger(R.integer.grid_items)) - val progressbar = view.findViewById(R.id.progressBar) fetchJson(progressbar,recyclerView) @@ -82,31 +81,7 @@ class Home : Fragment() { } private fun fetchJson(progressBar: ProgressBar, recyclerView: RecyclerView) { - //val client = OkHttpClient() - fun run() { -/* val request = Request.Builder() - .url("http://piped-api.alefvanoon.xyz/trending?region=US") - .build() - client.newCall(request).enqueue(object : Callback { - override fun onFailure(call: Call, e: IOException) { - e.printStackTrace() - } - override fun onResponse(call: Call, response: Response) { - response.use { - if (!response.isSuccessful) throw IOException("Unexpected code $response") - val body = response.body!!.string() - println(body) - val gson = GsonBuilder().create() - val itemType = object : TypeToken>() {}.type - val trendingList = gson.fromJson>(body, itemType) - runOnUiThread { - progressBar.visibility = View.GONE - recyclerView.adapter = TrendingAdapter(trendingList) - } - } - } - })*/ lifecycleScope.launchWhenCreated { val response = try { RetrofitInstance.api.getTrending("US") @@ -127,7 +102,7 @@ class Home : Fragment() { run() } - fun Fragment?.runOnUiThread(action: () -> Unit) { + private fun Fragment?.runOnUiThread(action: () -> Unit) { this ?: return if (!isAdded) return // Fragment not attached to an Activity activity?.runOnUiThread(action) diff --git a/app/src/main/java/xyz/btcland/libretube/SearchFragment.kt b/app/src/main/java/xyz/btcland/libretube/SearchFragment.kt index d3081651d..311c3a97e 100644 --- a/app/src/main/java/xyz/btcland/libretube/SearchFragment.kt +++ b/app/src/main/java/xyz/btcland/libretube/SearchFragment.kt @@ -11,10 +11,13 @@ import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.AutoCompleteTextView import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.RecyclerView import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch import retrofit2.HttpException +import xyz.btcland.libretube.adapters.SearchAdapter import java.io.IOException // TODO: Rename parameter arguments, choose names that match @@ -51,6 +54,8 @@ class SearchFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + val recyclerView = view.findViewById(R.id.search_recycler) + recyclerView.layoutManager = GridLayoutManager(view.context, 1) val autoTextView = view.findViewById(R.id.autoCompleteTextView) autoTextView.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged( @@ -64,10 +69,10 @@ class SearchFragment : Fragment() { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { if(s!! != ""){ - lifecycleScope.launchWhenCreated { + GlobalScope.launch { fetchSuggestions(s.toString(), autoTextView) delay(2000) - fetchSearch(s.toString()) + fetchSearch(s.toString(),recyclerView) } } } @@ -77,10 +82,6 @@ class SearchFragment : Fragment() { } }) - - - - } private fun fetchSuggestions(query: String, autoTextView: AutoCompleteTextView){ @@ -99,7 +100,7 @@ class SearchFragment : Fragment() { autoTextView.setAdapter(adapter) } } - private fun fetchSearch(query: String){ + private fun fetchSearch(query: String, recyclerView: RecyclerView){ lifecycleScope.launchWhenCreated { val response = try { RetrofitInstance.api.getSearchResults(query, "all") @@ -112,8 +113,11 @@ class SearchFragment : Fragment() { return@launchWhenCreated } if(response.items!!.isNotEmpty()){ - print(response!!.items!![0]) + runOnUiThread { + recyclerView.adapter = SearchAdapter(response.items) + } } + } } companion object { diff --git a/app/src/main/java/xyz/btcland/libretube/adapters/SearchAdapter.kt b/app/src/main/java/xyz/btcland/libretube/adapters/SearchAdapter.kt new file mode 100644 index 000000000..108d41d58 --- /dev/null +++ b/app/src/main/java/xyz/btcland/libretube/adapters/SearchAdapter.kt @@ -0,0 +1,110 @@ +package xyz.btcland.libretube.adapters + + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import android.widget.VideoView +import androidx.appcompat.app.AppCompatActivity +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.recyclerview.widget.RecyclerView +import com.squareup.picasso.Picasso +import xyz.btcland.libretube.PlayerFragment +import xyz.btcland.libretube.R +import xyz.btcland.libretube.obj.SearchItem +import xyz.btcland.libretube.obj.StreamItem +import xyz.btcland.libretube.videoViews + + +class SearchAdapter(private val searchItems: List): RecyclerView.Adapter() { + override fun getItemCount(): Int { + return searchItems.size + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder1 { + val layout = when (viewType) { + 0 -> R.layout.video_search_row + 1 -> R.layout.channel_search_row + 2 -> R.layout.playlist_search_row + else -> throw IllegalArgumentException("Invalid type") + } + val layoutInflater = LayoutInflater.from(parent.context) + val cell = layoutInflater.inflate(layout,parent,false) + return CustomViewHolder1(cell) + } + + override fun onBindViewHolder(holder: CustomViewHolder1, position: Int) { + holder.bind(searchItems[position]) + } + + override fun getItemViewType(position: Int): Int { + return when { + searchItems[position].url!!.startsWith("/watch",false) -> 0 + searchItems[position].url!!.startsWith("/channel",false) -> 1 + searchItems[position].url!!.startsWith("/playlist",false) -> 2 + else -> 3 + } + } +} +class CustomViewHolder1(private val v: View): RecyclerView.ViewHolder(v){ + + private fun bindWatch(item: SearchItem) { + val thumbnailImage = v.findViewById(R.id.search_thumbnail) + Picasso.get().load(item.thumbnail).into(thumbnailImage) + val channelImage = v.findViewById(R.id.search_channel_image) + Picasso.get().load(item.uploaderAvatar).into(channelImage) + val title = v.findViewById(R.id.search_description) + title.text = item.title + val views = v.findViewById(R.id.search_views) + views.text = item.views.videoViews() +" • "+item.uploadedDate + val channelName = v.findViewById(R.id.search_channel_name) + channelName.text = item.uploaderName + v.setOnClickListener{ + var bundle = Bundle() + bundle.putString("videoId",item.url!!.replace("/watch?v=","")) + var frag = PlayerFragment() + frag.arguments = bundle + val activity = v.context as AppCompatActivity + activity.supportFragmentManager.beginTransaction() + .remove(PlayerFragment()) + .commit() + activity.supportFragmentManager.beginTransaction() + .replace(R.id.container, frag) + .commitNow() + } + } + private fun bindChannel(item: SearchItem) { + val channelImage = v.findViewById(R.id.search_channel_image) + Picasso.get().load(item.thumbnail).into(channelImage) + val channelName = v.findViewById(R.id.search_channel_name) + channelName.text = item.name + val channelViews = v.findViewById(R.id.search_views) + channelViews.text = item.subscribers.videoViews() + "subscribers • "+ item.videos + " videos" + //todo sub button + } + private fun bindPlaylist(item: SearchItem) { + val playlistImage = v.findViewById(R.id.search_thumbnail) + Picasso.get().load(item.thumbnail).into(playlistImage) + val playlistNumber = v.findViewById(R.id.search_playlist_number) + playlistNumber.text = item.videos.toString() + val playlistName = v.findViewById(R.id.search_description) + playlistName.text = item.name + val playlistChannelName = v.findViewById(R.id.search_name) + playlistChannelName.text = item.uploaderName + val playlistVideosNumber = v.findViewById(R.id.search_playlist_videos) + playlistVideosNumber.text = item.videos.toString()+" videos" + } + + fun bind(searchItem: SearchItem) { + when { + searchItem.url!!.startsWith("/watch",false) -> bindWatch(searchItem) + searchItem.url!!.startsWith("/channel",false) -> bindChannel(searchItem) + searchItem.url!!.startsWith("/playlist",false) -> bindPlaylist(searchItem) + else -> { + } + } + } +} diff --git a/app/src/main/java/xyz/btcland/libretube/TrendingAdapter.kt b/app/src/main/java/xyz/btcland/libretube/adapters/TrendingAdapter.kt similarity index 90% rename from app/src/main/java/xyz/btcland/libretube/TrendingAdapter.kt rename to app/src/main/java/xyz/btcland/libretube/adapters/TrendingAdapter.kt index 8618dde6e..b67bf516b 100644 --- a/app/src/main/java/xyz/btcland/libretube/TrendingAdapter.kt +++ b/app/src/main/java/xyz/btcland/libretube/adapters/TrendingAdapter.kt @@ -1,4 +1,4 @@ -package xyz.btcland.libretube +package xyz.btcland.libretube.adapters import android.os.Bundle import android.view.LayoutInflater @@ -9,7 +9,10 @@ import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.RecyclerView import com.squareup.picasso.Picasso +import xyz.btcland.libretube.PlayerFragment +import xyz.btcland.libretube.R import xyz.btcland.libretube.obj.StreamItem +import xyz.btcland.libretube.videoViews class TrendingAdapter(private val videoFeed: List): RecyclerView.Adapter() { override fun getItemCount(): Int { @@ -30,13 +33,11 @@ class TrendingAdapter(private val videoFeed: List): RecyclerView.Ada val channelImage = holder.v.findViewById(R.id.channel_image) channelImage.setOnClickListener{ println("channel clicked") + //TODO } Picasso.get().load(trending.thumbnail).into(thumbnailImage) Picasso.get().load(trending.uploaderAvatar).into(channelImage) holder.v.setOnClickListener{ - //val intent = Intent(holder.v.context, Player::class.java) - //intent.putExtra("videoId",trending.url.replace("/watch?v=","")) - //holder.v.context.startActivity(intent) var bundle = Bundle() bundle.putString("videoId",trending.url!!.replace("/watch?v=","")) var frag = PlayerFragment() diff --git a/app/src/main/java/xyz/btcland/libretube/obj/SearchItem.kt b/app/src/main/java/xyz/btcland/libretube/obj/SearchItem.kt index 16a330e9d..986c21723 100644 --- a/app/src/main/java/xyz/btcland/libretube/obj/SearchItem.kt +++ b/app/src/main/java/xyz/btcland/libretube/obj/SearchItem.kt @@ -2,16 +2,17 @@ package xyz.btcland.libretube.obj data class SearchItem( var url: String?, - var title: String?, var thumbnail: String?, var uploaderName: String?, + //Video only attributes + var title: String?, var uploaderUrl: String?, var uploaderAvatar: String?, var uploadedDate: String?, var duration: Long?, var views: Long?, var uploaderVerified: Boolean?, -//Channel and Playlist attributes + //Channel and Playlist attributes var name: String? = null, var description: String? = null, var subscribers: Long? = -1, diff --git a/app/src/main/res/drawable/ic_playlist.xml b/app/src/main/res/drawable/ic_playlist.xml new file mode 100644 index 000000000..84a357ccf --- /dev/null +++ b/app/src/main/res/drawable/ic_playlist.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/layout/channel_search_row.xml b/app/src/main/res/layout/channel_search_row.xml new file mode 100644 index 000000000..23ab51b9f --- /dev/null +++ b/app/src/main/res/layout/channel_search_row.xml @@ -0,0 +1,53 @@ + + + + + + + + + +