mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 23:40:33 +05:30
search fragment + suggestion
This commit is contained in:
parent
c356f9b426
commit
21d29ad4af
3
.idea/misc.xml
generated
3
.idea/misc.xml
generated
@ -5,6 +5,7 @@
|
|||||||
<map>
|
<map>
|
||||||
<entry key="app/src/main/res/drawable/ic_close.xml" value="0.17135416666666667" />
|
<entry key="app/src/main/res/drawable/ic_close.xml" value="0.17135416666666667" />
|
||||||
<entry key="app/src/main/res/drawable/ic_play.xml" value="0.17135416666666667" />
|
<entry key="app/src/main/res/drawable/ic_play.xml" value="0.17135416666666667" />
|
||||||
|
<entry key="app/src/main/res/drawable/ic_search2.xml" value="0.17135416666666667" />
|
||||||
<entry key="app/src/main/res/layout-land/fragment_player.xml" value="0.1" />
|
<entry key="app/src/main/res/layout-land/fragment_player.xml" value="0.1" />
|
||||||
<entry key="app/src/main/res/layout/activity_main.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/activity_main.xml" value="0.1" />
|
||||||
<entry key="app/src/main/res/layout/activity_player.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/activity_player.xml" value="0.1" />
|
||||||
@ -12,10 +13,12 @@
|
|||||||
<entry key="app/src/main/res/layout/fragment_home.xml" value="0.16" />
|
<entry key="app/src/main/res/layout/fragment_home.xml" value="0.16" />
|
||||||
<entry key="app/src/main/res/layout/fragment_library.xml" value="0.11956521739130435" />
|
<entry key="app/src/main/res/layout/fragment_library.xml" value="0.11956521739130435" />
|
||||||
<entry key="app/src/main/res/layout/fragment_player.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/fragment_player.xml" value="0.1" />
|
||||||
|
<entry key="app/src/main/res/layout/fragment_search.xml" value="0.25" />
|
||||||
<entry key="app/src/main/res/layout/fragment_subscriptions.xml" value="0.1" />
|
<entry key="app/src/main/res/layout/fragment_subscriptions.xml" value="0.1" />
|
||||||
<entry key="app/src/main/res/layout/styled_quality_list.xml" value="0.15520833333333334" />
|
<entry key="app/src/main/res/layout/styled_quality_list.xml" value="0.15520833333333334" />
|
||||||
<entry key="app/src/main/res/layout/styled_quality_list_item.xml" value="0.15520833333333334" />
|
<entry key="app/src/main/res/layout/styled_quality_list_item.xml" value="0.15520833333333334" />
|
||||||
<entry key="app/src/main/res/layout/trending_row.xml" value="0.33" />
|
<entry key="app/src/main/res/layout/trending_row.xml" value="0.33" />
|
||||||
|
<entry key="app/src/main/res/menu/action_bar.xml" value="0.2" />
|
||||||
<entry key="app/src/main/res/menu/bottom_menu.xml" value="0.15520833333333334" />
|
<entry key="app/src/main/res/menu/bottom_menu.xml" value="0.15520833333333334" />
|
||||||
<entry key="app/src/main/res/xml-land/player_scene.xml" value="0.13139329805996472" />
|
<entry key="app/src/main/res/xml-land/player_scene.xml" value="0.13139329805996472" />
|
||||||
<entry key="app/src/main/res/xml/network_security_config.xml" value="0.15520833333333334" />
|
<entry key="app/src/main/res/xml/network_security_config.xml" value="0.15520833333333334" />
|
||||||
|
@ -50,5 +50,6 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-jackson:2.9.0'
|
||||||
|
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.0'
|
||||||
|
|
||||||
}
|
}
|
@ -3,23 +3,51 @@ package xyz.btcland.libretube
|
|||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.Menu
|
||||||
|
import android.view.MenuItem
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import androidx.constraintlayout.motion.widget.MotionLayout
|
import androidx.constraintlayout.motion.widget.MotionLayout
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.navigation.Navigation
|
||||||
import com.google.android.material.bottomnavigation.BottomNavigationView
|
import com.google.android.material.bottomnavigation.BottomNavigationView
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.navigation.ui.NavigationUI.onNavDestinationSelected
|
||||||
import androidx.navigation.ui.setupWithNavController
|
import androidx.navigation.ui.setupWithNavController
|
||||||
import com.google.android.exoplayer2.ExoPlayer
|
import com.google.android.exoplayer2.ExoPlayer
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
lateinit var exoPlayer:ExoPlayer
|
lateinit var bottomNavigationView: BottomNavigationView
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNav)
|
bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottomNav)
|
||||||
val navController = findNavController(R.id.fragment)
|
val navController = findNavController(R.id.fragment)
|
||||||
bottomNavigationView.setupWithNavController(navController)
|
bottomNavigationView.setupWithNavController(navController)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId){
|
||||||
|
R.id.action_search -> {
|
||||||
|
val navController = findNavController(R.id.fragment)
|
||||||
|
navController.popBackStack()
|
||||||
|
navController.navigate(R.id.searchFragment)
|
||||||
|
//bottomNavigationView.clearFocus()
|
||||||
|
//val navController = findNavController(R.id.fragment)
|
||||||
|
//navController.navigate(R.id.searchFragment)
|
||||||
|
//navController.navigate(R.id.home2)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.action_settings -> {
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||||
|
menuInflater.inflate(R.menu.action_bar,menu)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||||
|
@ -12,4 +12,13 @@ interface PipedApi {
|
|||||||
|
|
||||||
@GET("streams/{videoId}")
|
@GET("streams/{videoId}")
|
||||||
suspend fun getStreams(@Path("videoId") videoId: String): Streams
|
suspend fun getStreams(@Path("videoId") videoId: String): Streams
|
||||||
|
|
||||||
|
@GET("search")
|
||||||
|
suspend fun getSearchResults(
|
||||||
|
@Query("q") searchQuery: String,
|
||||||
|
@Query("filter") filer: String
|
||||||
|
): List<StreamItem>
|
||||||
|
|
||||||
|
@GET("suggestions")
|
||||||
|
suspend fun getSuggestions(@Query("query") query: String): List<String>
|
||||||
}
|
}
|
@ -230,6 +230,7 @@ class PlayerFragment : Fragment() {
|
|||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
try {
|
try {
|
||||||
|
(activity as MainActivity).supportActionBar?.show()
|
||||||
exoPlayer.stop()
|
exoPlayer.stop()
|
||||||
}catch (e: Exception){}
|
}catch (e: Exception){}
|
||||||
|
|
||||||
@ -256,135 +257,7 @@ class PlayerFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun fetchJson(view: View) {
|
private fun fetchJson(view: View) {
|
||||||
//val client = OkHttpClient()
|
|
||||||
|
|
||||||
fun run() {
|
fun run() {
|
||||||
/* val request = Request.Builder()
|
|
||||||
.url("http://piped-api.alefvanoon.xyz/streams/$videoId")
|
|
||||||
.build()
|
|
||||||
*//* val retrofit = Retrofit.Builder()
|
|
||||||
.baseUrl("http://piped-api.alefvanoon.xyz/")
|
|
||||||
.addConverterFactory(JacksonConverterFactory.create())
|
|
||||||
.build()
|
|
||||||
|
|
||||||
val videoInPlayer2 = retrofit.create(vidVid::class.java).vidIn(videoId)*//*
|
|
||||||
|
|
||||||
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 videoInPlayer = gson.fromJson(body, VideoInPlayer::class.java)
|
|
||||||
var videosNameArray: Array<CharSequence> = arrayOf()
|
|
||||||
videosNameArray += "HLS"
|
|
||||||
for (vid in videoInPlayer.videoStreams){
|
|
||||||
val name = vid.quality +" "+ vid.format
|
|
||||||
videosNameArray += name
|
|
||||||
}
|
|
||||||
runOnUiThread {
|
|
||||||
var subtitle = mutableListOf<SubtitleConfiguration>()
|
|
||||||
if(videoInPlayer.subtitles.isNotEmpty()){
|
|
||||||
subtitle?.add(SubtitleConfiguration.Builder(videoInPlayer.subtitles[0].url.toUri())
|
|
||||||
.setMimeType(videoInPlayer.subtitles[0].mimeType) // The correct MIME type (required).
|
|
||||||
.setLanguage(videoInPlayer.subtitles[0].code) // The subtitle language (optional).
|
|
||||||
.build())}
|
|
||||||
val mediaItem: MediaItem = MediaItem.Builder()
|
|
||||||
.setUri(videoInPlayer.hls)
|
|
||||||
.setSubtitleConfigurations(subtitle)
|
|
||||||
.build()
|
|
||||||
exoPlayer = ExoPlayer.Builder(view.context)
|
|
||||||
.build()
|
|
||||||
exoPlayerView.setShowSubtitleButton(true)
|
|
||||||
exoPlayerView.setShowNextButton(false)
|
|
||||||
exoPlayerView.setShowPreviousButton(false)
|
|
||||||
//exoPlayerView.controllerShowTimeoutMs = 1500
|
|
||||||
exoPlayerView.controllerHideOnTouch = true
|
|
||||||
exoPlayerView.player = exoPlayer
|
|
||||||
exoPlayer.setMediaItem(mediaItem)
|
|
||||||
///exoPlayer.getMediaItemAt(5)
|
|
||||||
exoPlayer.prepare()
|
|
||||||
exoPlayer.play()
|
|
||||||
|
|
||||||
view.findViewById<TextView>(R.id.title_textView).text = videoInPlayer.title
|
|
||||||
|
|
||||||
view.findViewById<ImageButton>(R.id.quality_select).setOnClickListener{
|
|
||||||
//Dialog for quality selection
|
|
||||||
val builder: AlertDialog.Builder? = activity?.let {
|
|
||||||
AlertDialog.Builder(it)
|
|
||||||
}
|
|
||||||
builder!!.setTitle(R.string.choose_quality_dialog)
|
|
||||||
.setItems(videosNameArray,
|
|
||||||
DialogInterface.OnClickListener { _, which ->
|
|
||||||
whichQuality = which
|
|
||||||
if(videoInPlayer.subtitles.isNotEmpty()) {
|
|
||||||
var subtitle =
|
|
||||||
mutableListOf<SubtitleConfiguration>()
|
|
||||||
subtitle?.add(
|
|
||||||
SubtitleConfiguration.Builder(videoInPlayer.subtitles[0].url.toUri())
|
|
||||||
.setMimeType(videoInPlayer.subtitles[0].mimeType) // The correct MIME type (required).
|
|
||||||
.setLanguage(videoInPlayer.subtitles[0].code) // The subtitle language (optional).
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if(which==0){
|
|
||||||
val mediaItem: MediaItem = MediaItem.Builder()
|
|
||||||
.setUri(videoInPlayer.hls)
|
|
||||||
.setSubtitleConfigurations(subtitle)
|
|
||||||
.build()
|
|
||||||
exoPlayer.setMediaItem(mediaItem)
|
|
||||||
}else{
|
|
||||||
val dataSourceFactory: DataSource.Factory =
|
|
||||||
DefaultHttpDataSource.Factory()
|
|
||||||
val videoItem: MediaItem = MediaItem.Builder()
|
|
||||||
.setUri(videoInPlayer.videoStreams[which-1].url)
|
|
||||||
.setSubtitleConfigurations(subtitle)
|
|
||||||
.build()
|
|
||||||
val videoSource: MediaSource = DefaultMediaSourceFactory(dataSourceFactory)
|
|
||||||
.createMediaSource(videoItem)
|
|
||||||
var audioSource: MediaSource = DefaultMediaSourceFactory(dataSourceFactory)
|
|
||||||
.createMediaSource(fromUri(videoInPlayer.audioStreams[0].url))
|
|
||||||
if (videoInPlayer.videoStreams[which-1].quality=="720p" || videoInPlayer.videoStreams[which-1].quality=="1080p" || videoInPlayer.videoStreams[which-1].quality=="480p" ){
|
|
||||||
audioSource = ProgressiveMediaSource.Factory(dataSourceFactory)
|
|
||||||
.createMediaSource(fromUri(videoInPlayer.audioStreams[getMostBitRate(videoInPlayer.audioStreams)].url))
|
|
||||||
//println("fuckkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkitttttttttttttttttttttt")
|
|
||||||
}
|
|
||||||
val mergeSource: MediaSource = MergingMediaSource(videoSource,audioSource)
|
|
||||||
exoPlayer.setMediaSource(mergeSource)
|
|
||||||
}
|
|
||||||
view.findViewById<TextView>(R.id.quality_text).text=videosNameArray[which]
|
|
||||||
})
|
|
||||||
val dialog: AlertDialog? = builder?.create()
|
|
||||||
dialog?.show()
|
|
||||||
}
|
|
||||||
//Listener for play and pause icon change
|
|
||||||
exoPlayer!!.addListener(object : com.google.android.exoplayer2.Player.Listener {
|
|
||||||
override fun onPlayerStateChanged(playWhenReady: Boolean,playbackState: Int) {
|
|
||||||
if (playWhenReady && playbackState == Player.STATE_READY) {
|
|
||||||
// media actually playing
|
|
||||||
view.findViewById<ImageView>(R.id.play_imageView).setImageResource(R.drawable.ic_pause)
|
|
||||||
} else if (playWhenReady) {
|
|
||||||
// might be idle (plays after prepare()),
|
|
||||||
// buffering (plays when data available)
|
|
||||||
// or ended (plays when seek away from end)
|
|
||||||
view.findViewById<ImageView>(R.id.play_imageView).setImageResource(R.drawable.ic_play)
|
|
||||||
} else {
|
|
||||||
// player paused in any state
|
|
||||||
view.findViewById<ImageView>(R.id.play_imageView).setImageResource(R.drawable.ic_play)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
})*/
|
|
||||||
|
|
||||||
lifecycleScope.launchWhenCreated {
|
lifecycleScope.launchWhenCreated {
|
||||||
val response = try {
|
val response = try {
|
||||||
RetrofitInstance.api.getStreams(videoId!!)
|
RetrofitInstance.api.getStreams(videoId!!)
|
||||||
|
123
app/src/main/java/xyz/btcland/libretube/SearchFragment.kt
Normal file
123
app/src/main/java/xyz/btcland/libretube/SearchFragment.kt
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
package xyz.btcland.libretube
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ArrayAdapter
|
||||||
|
import android.widget.AutoCompleteTextView
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import retrofit2.HttpException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
// TODO: Rename parameter arguments, choose names that match
|
||||||
|
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
|
||||||
|
private const val ARG_PARAM1 = "param1"
|
||||||
|
private const val ARG_PARAM2 = "param2"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple [Fragment] subclass.
|
||||||
|
* Use the [SearchFragment.newInstance] factory method to
|
||||||
|
* create an instance of this fragment.
|
||||||
|
*/
|
||||||
|
class SearchFragment : Fragment() {
|
||||||
|
// TODO: Rename and change types of parameters
|
||||||
|
private var param1: String? = null
|
||||||
|
private var param2: String? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
arguments?.let {
|
||||||
|
param1 = it.getString(ARG_PARAM1)
|
||||||
|
param2 = it.getString(ARG_PARAM2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
// Inflate the layout for this fragment
|
||||||
|
return inflater.inflate(R.layout.fragment_search, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
val autoTextView = view.findViewById<AutoCompleteTextView>(R.id.autoCompleteTextView)
|
||||||
|
autoTextView.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(
|
||||||
|
s: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
count: Int,
|
||||||
|
after: Int
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
if(s!! != ""){
|
||||||
|
println(s.toString())
|
||||||
|
fetchSuggestions(s.toString(), autoTextView)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchSuggestions(query: String, autoTextView: AutoCompleteTextView){
|
||||||
|
lifecycleScope.launchWhenCreated {
|
||||||
|
val response = try {
|
||||||
|
RetrofitInstance.api.getSuggestions(query)
|
||||||
|
} catch (e: IOException) {
|
||||||
|
println(e)
|
||||||
|
Log.e(TAG, "IOException, you might not have internet connection")
|
||||||
|
return@launchWhenCreated
|
||||||
|
} catch (e: HttpException) {
|
||||||
|
Log.e(TAG, "HttpException, unexpected response")
|
||||||
|
return@launchWhenCreated
|
||||||
|
}
|
||||||
|
val adapter = ArrayAdapter(context!!, android.R.layout.simple_list_item_1, response)
|
||||||
|
autoTextView.setAdapter(adapter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Use this factory method to create a new instance of
|
||||||
|
* this fragment using the provided parameters.
|
||||||
|
*
|
||||||
|
* @param param1 Parameter 1.
|
||||||
|
* @param param2 Parameter 2.
|
||||||
|
* @return A new instance of fragment SearchFragment.
|
||||||
|
*/
|
||||||
|
// TODO: Rename and change types and number of parameters
|
||||||
|
@JvmStatic
|
||||||
|
fun newInstance(param1: String, param2: String) =
|
||||||
|
SearchFragment().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putString(ARG_PARAM1, param1)
|
||||||
|
putString(ARG_PARAM2, param2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Fragment?.runOnUiThread(action: () -> Unit) {
|
||||||
|
this ?: return
|
||||||
|
if (!isAdded) return // Fragment not attached to an Activity
|
||||||
|
activity?.runOnUiThread(action)
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +0,0 @@
|
|||||||
package xyz.btcland.libretube.obj;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class Playlist {
|
|
||||||
|
|
||||||
public String name, thumbnailUrl, bannerUrl, nextpage, uploader, uploaderUrl, uploaderAvatar;
|
|
||||||
public int videos;
|
|
||||||
public List<StreamItem> relatedStreams;
|
|
||||||
|
|
||||||
public Playlist(String name, String thumbnailUrl, String bannerUrl, String nextpage, String uploader,
|
|
||||||
String uploaderUrl, String uploaderAvatar, int videos, List<StreamItem> relatedStreams) {
|
|
||||||
this.name = name;
|
|
||||||
this.thumbnailUrl = thumbnailUrl;
|
|
||||||
this.bannerUrl = bannerUrl;
|
|
||||||
this.nextpage = nextpage;
|
|
||||||
this.videos = videos;
|
|
||||||
this.uploader = uploader;
|
|
||||||
this.uploaderUrl = uploaderUrl;
|
|
||||||
this.uploaderAvatar = uploaderAvatar;
|
|
||||||
this.relatedStreams = relatedStreams;
|
|
||||||
}
|
|
||||||
}
|
|
13
app/src/main/java/xyz/btcland/libretube/obj/Playlist.kt
Normal file
13
app/src/main/java/xyz/btcland/libretube/obj/Playlist.kt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package xyz.btcland.libretube.obj
|
||||||
|
|
||||||
|
data class Playlist(
|
||||||
|
var name: String? = null,
|
||||||
|
var thumbnailUrl: String? = null,
|
||||||
|
var bannerUrl: String? = null,
|
||||||
|
var nextpage: String? = null,
|
||||||
|
var uploader: String? = null,
|
||||||
|
var uploaderUrl: String? = null,
|
||||||
|
var uploaderAvatar: String? = null,
|
||||||
|
var videos: Int = 0,
|
||||||
|
var relatedStreams: List<StreamItem?>? = null,
|
||||||
|
)
|
@ -1,5 +1,15 @@
|
|||||||
package xyz.btcland.libretube.obj
|
package xyz.btcland.libretube.obj
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes
|
||||||
|
import com.fasterxml.jackson.annotation.JsonTypeInfo
|
||||||
|
import xyz.btcland.libretube.obj.search.SearchChannel
|
||||||
|
import xyz.btcland.libretube.obj.search.SearchPlaylist
|
||||||
|
|
||||||
|
@JsonTypeInfo(use = JsonTypeInfo.Id.DEDUCTION)
|
||||||
|
@JsonSubTypes(value =[
|
||||||
|
JsonSubTypes.Type(SearchChannel::class),
|
||||||
|
JsonSubTypes.Type(SearchPlaylist::class)
|
||||||
|
])
|
||||||
data class StreamItem(
|
data class StreamItem(
|
||||||
var url: String?,
|
var url: String?,
|
||||||
var title: String?,
|
var title: String?,
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package xyz.btcland.libretube.obj.search
|
||||||
|
|
||||||
|
data class SearchChannel(
|
||||||
|
var name: String? = null,
|
||||||
|
var thumbnail: String? = null,
|
||||||
|
var url: String? = null,
|
||||||
|
var description: String? = null,
|
||||||
|
var subscribers: Long? = -1,
|
||||||
|
var videos: Long? = -1,
|
||||||
|
var verified: Boolean? = null
|
||||||
|
)
|
@ -0,0 +1,10 @@
|
|||||||
|
package xyz.btcland.libretube.obj.search
|
||||||
|
|
||||||
|
data class SearchPlaylist(
|
||||||
|
var name: String? = null,
|
||||||
|
var thumbnail: String? = null,
|
||||||
|
var url: String? = null,
|
||||||
|
var uploaderName: String? =null,
|
||||||
|
var videos: Long = -1
|
||||||
|
|
||||||
|
)
|
5
app/src/main/res/drawable/ic_search.xml
Normal file
5
app/src/main/res/drawable/ic_search.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
10
app/src/main/res/drawable/ic_search2.xml
Normal file
10
app/src/main/res/drawable/ic_search2.xml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="35dp"
|
||||||
|
android:height="35dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_settings.xml
Normal file
5
app/src/main/res/drawable/ic_settings.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/>
|
||||||
|
</vector>
|
45
app/src/main/res/layout/fragment_search.xml
Normal file
45
app/src/main/res/layout/fragment_search.xml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?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="match_parent"
|
||||||
|
tools:context=".SearchFragment">
|
||||||
|
|
||||||
|
<AutoCompleteTextView
|
||||||
|
android:id="@+id/autoCompleteTextView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="7dp"
|
||||||
|
android:hint="@string/search_hint"
|
||||||
|
android:imeOptions="actionSearch"
|
||||||
|
android:inputType="text"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:minHeight="48dp"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/imageView_Search"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView_Search"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="7dp"
|
||||||
|
android:src="@drawable/ic_search2"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/search_recycler"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="8dp"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/autoCompleteTextView" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
15
app/src/main/res/menu/action_bar.xml
Normal file
15
app/src/main/res/menu/action_bar.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item android:title="Search"
|
||||||
|
android:icon="@drawable/ic_search"
|
||||||
|
android:id="@+id/action_search"
|
||||||
|
app:showAsAction="always"
|
||||||
|
/>
|
||||||
|
<item android:title="Settings"
|
||||||
|
android:icon="@drawable/ic_settings"
|
||||||
|
android:id="@+id/action_settings"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
/>
|
||||||
|
</menu>
|
@ -4,5 +4,6 @@
|
|||||||
<item android:id="@+id/home2" android:title="Home" android:icon="@drawable/ic_home"/>
|
<item android:id="@+id/home2" android:title="Home" android:icon="@drawable/ic_home"/>
|
||||||
<item android:id="@+id/subscriptions" android:title="Subscriptions" android:icon="@drawable/ic_subscriptions"/>
|
<item android:id="@+id/subscriptions" android:title="Subscriptions" android:icon="@drawable/ic_subscriptions"/>
|
||||||
<item android:id="@+id/library" android:title="Library" android:icon="@drawable/ic_library"/>
|
<item android:id="@+id/library" android:title="Library" android:icon="@drawable/ic_library"/>
|
||||||
|
<item android:id="@+id/searchFragment" android:visible="false"/>
|
||||||
|
|
||||||
</menu>
|
</menu>
|
@ -9,15 +9,32 @@
|
|||||||
android:id="@+id/home2"
|
android:id="@+id/home2"
|
||||||
android:name="xyz.btcland.libretube.Home"
|
android:name="xyz.btcland.libretube.Home"
|
||||||
android:label="fragment_home"
|
android:label="fragment_home"
|
||||||
tools:layout="@layout/fragment_home" />
|
tools:layout="@layout/fragment_home" >
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_home2_to_searchFragment"
|
||||||
|
app:destination="@id/searchFragment" />
|
||||||
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/subscriptions"
|
android:id="@+id/subscriptions"
|
||||||
android:name="xyz.btcland.libretube.Subscriptions"
|
android:name="xyz.btcland.libretube.Subscriptions"
|
||||||
android:label="fragment_subscriptions"
|
android:label="fragment_subscriptions"
|
||||||
tools:layout="@layout/fragment_subscriptions" />
|
tools:layout="@layout/fragment_subscriptions" >
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_subscriptions_to_searchFragment"
|
||||||
|
app:destination="@id/searchFragment" />
|
||||||
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/library"
|
android:id="@+id/library"
|
||||||
android:name="xyz.btcland.libretube.Library"
|
android:name="xyz.btcland.libretube.Library"
|
||||||
android:label="fragment_library"
|
android:label="fragment_library"
|
||||||
tools:layout="@layout/fragment_library" />
|
tools:layout="@layout/fragment_library" >
|
||||||
|
<action
|
||||||
|
android:id="@+id/action_library_to_searchFragment"
|
||||||
|
app:destination="@id/searchFragment" />
|
||||||
|
</fragment>
|
||||||
|
<fragment
|
||||||
|
android:id="@+id/searchFragment"
|
||||||
|
android:name="xyz.btcland.libretube.SearchFragment"
|
||||||
|
android:label="fragment_search"
|
||||||
|
tools:layout="@layout/fragment_search" />
|
||||||
</navigation>
|
</navigation>
|
@ -1,6 +1,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">LibreTube</string>
|
<string name="app_name">LibreTube</string>
|
||||||
<!-- TODO: Remove or change this placeholder text -->
|
|
||||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
<string name="choose_quality_dialog">Choose Quality:</string>
|
<string name="choose_quality_dialog">Choose Quality:</string>
|
||||||
|
<string name="search_hint">Search</string>
|
||||||
</resources>
|
</resources>
|
Loading…
x
Reference in New Issue
Block a user