add tv shows support to parser, start implementing account fragment

This commit is contained in:
Jannik 2020-10-11 23:16:47 +02:00
parent b27a218b51
commit 7b708f5aa0
Signed by: Seil0
GPG Key ID: E8459F3723C52C24
18 changed files with 330 additions and 65 deletions

View File

@ -14,6 +14,7 @@ android {
versionName "0.1-alpha1" versionName "0.1-alpha1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resValue "string", "build_time", buildTime()
setProperty("archivesBaseName", "teapod-$versionName") setProperty("archivesBaseName", "teapod-$versionName")
} }
@ -62,4 +63,8 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
static def buildTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
} }

View File

@ -79,7 +79,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
Log.i(javaClass.name, "please login!") Log.i(javaClass.name, "please login!")
LoginDialog(this).positiveButton { LoginDialog(this).positiveButton {
EncryptedPreferences.saveCredentials(email, password, context) EncryptedPreferences.saveCredentials(login, password, context)
}.negativeButton { }.negativeButton {
Log.i(javaClass.name, "Login canceled, exiting.") Log.i(javaClass.name, "Login canceled, exiting.")
finish() finish()
@ -88,17 +88,14 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS
} }
fun showDetailFragment(media: GUIMedia) { fun showDetailFragment(media: GUIMedia) {
val streams = AoDParser().loadStreams(media.link) // load the streams for the selected media val streamMedia = AoDParser().loadStreams(media.link) // load the streams for the selected media
val mediaFragment = MediaFragment(media, streams) val mediaFragment = MediaFragment(media, streamMedia)
supportFragmentManager.commit { supportFragmentManager.commit {
add(R.id.nav_host_fragment, mediaFragment, "MediaFragment") add(R.id.nav_host_fragment, mediaFragment, "MediaFragment")
addToBackStack(null) addToBackStack(null)
show(mediaFragment) show(mediaFragment)
} }
println("visible !!!: " + mediaFragment.isVisible)
println(supportFragmentManager.backStackEntryCount)
} }
fun startPlayer(streamUrl: String) { fun startPlayer(streamUrl: String) {

View File

@ -1,11 +1,15 @@
package org.mosad.teapod.parser package org.mosad.teapod.parser
import android.util.Log
import com.google.gson.JsonParser import com.google.gson.JsonParser
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.jsoup.Connection import org.jsoup.Connection
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.mosad.teapod.preferences.EncryptedPreferences import org.mosad.teapod.preferences.EncryptedPreferences
import org.mosad.teapod.util.DataTypes.MediaType
import org.mosad.teapod.util.GUIMedia import org.mosad.teapod.util.GUIMedia
import org.mosad.teapod.util.StreamMedia
import kotlin.collections.ArrayList
class AoDParser { class AoDParser {
@ -60,7 +64,9 @@ class AoDParser {
} }
} }
// https://www.anime-on-demand.de/animes /**
* list all animes from the website
*/
fun listAnimes(): ArrayList<GUIMedia> = runBlocking { fun listAnimes(): ArrayList<GUIMedia> = runBlocking {
if (sessionCookies.isEmpty()) login() if (sessionCookies.isEmpty()) login()
@ -92,12 +98,12 @@ class AoDParser {
/** /**
* load streams for the media path * load streams for the media path
*/ */
fun loadStreams(mediaPath: String): List<String> = runBlocking { fun loadStreams(mediaPath: String): StreamMedia = runBlocking {
if (sessionCookies.isEmpty()) login() if (sessionCookies.isEmpty()) login()
if (!loginSuccess) { if (!loginSuccess) {
println("please log in") // TODO println("please log in") // TODO
return@runBlocking listOf() return@runBlocking StreamMedia(MediaType.OTHER)
} }
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
@ -114,14 +120,20 @@ class AoDParser {
//println("first entry: ${playlists.first()}") //println("first entry: ${playlists.first()}")
//println("csrf token is: $csrfToken") //println("csrf token is: $csrfToken")
return@withContext loadStreamInfo(playlists.first(), csrfToken) val type = if (res.select("h2").eachText().filter { it == "Episoden" }.any()) {
MediaType.TVSHOW
} else {
MediaType.MOVIE
}
return@withContext loadStreamInfo(playlists.first(), csrfToken, type)
} }
} }
/** /**
* load the playlist path and parse it, read the stream info from json * load the playlist path and parse it, read the stream info from json
*/ */
private fun loadStreamInfo(playlistPath: String, csrfToken: String): List<String> = runBlocking { private fun loadStreamInfo(playlistPath: String, csrfToken: String, type: MediaType): StreamMedia = runBlocking {
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val headers = mutableMapOf( val headers = mutableMapOf(
Pair("Accept", "application/json, text/javascript, */*; q=0.01"), Pair("Accept", "application/json, text/javascript, */*; q=0.01"),
@ -139,13 +151,37 @@ class AoDParser {
//println(res.body()) //println(res.body())
// TODO if it's a series there sources for each episode println(type)
val sources = JsonParser.parseString(res.body()).asJsonObject return@withContext when (type) {
.get("playlist").asJsonArray.first().asJsonObject MediaType.MOVIE -> {
.get("sources").asJsonArray val movie = JsonParser.parseString(res.body()).asJsonObject
.get("playlist").asJsonArray
return@withContext sources.toList().map { val streamList = arrayListOf<String>()
it.asJsonObject.get("file").asString movie.first().asJsonObject.get("sources").asJsonArray.toList().forEach {
streamList.add(it.asJsonObject.get("file").asString)
}
StreamMedia(MediaType.MOVIE, streamList)
}
MediaType.TVSHOW -> {
val episodes = JsonParser.parseString(res.body()).asJsonObject
.get("playlist").asJsonArray
val streamList = arrayListOf<String>()
episodes.forEach {
val streamUrl = it.asJsonObject.get("sources").asJsonArray
.first().asJsonObject
.get("file").asString
streamList.add(streamUrl)
}
StreamMedia(MediaType.TVSHOW, streamList)
}
else -> {
Log.e(javaClass.name, "Wrong Type, please report this issue.")
StreamMedia(MediaType.OTHER)
}
} }
} }
} }

View File

@ -1,19 +1,23 @@
package org.mosad.teapod.ui package org.mosad.teapod.ui
import android.os.Bundle import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
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.ArrayAdapter
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.fragment_media.* import kotlinx.android.synthetic.main.fragment_media.*
import org.mosad.teapod.MainActivity import org.mosad.teapod.MainActivity
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.util.DataTypes.MediaType
import org.mosad.teapod.util.GUIMedia import org.mosad.teapod.util.GUIMedia
import java.net.URL import org.mosad.teapod.util.StreamMedia
import java.net.URLEncoder
class MediaFragment(val media: GUIMedia, val streams: List<String>) : Fragment() { class MediaFragment(private val guiMedia: GUIMedia, private val streamMedia: StreamMedia) : Fragment() {
private lateinit var adapterEpisodes: ArrayAdapter<String>
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_media, container, false) return inflater.inflate(R.layout.fragment_media, container, false)
@ -22,25 +26,51 @@ class MediaFragment(val media: GUIMedia, val streams: List<String>) : Fragment()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
// load poster // generic gui
Glide.with(requireContext()).load(media.posterLink).into(image_poster) Glide.with(requireContext()).load(guiMedia.posterLink).into(image_poster)
text_title.text = media.title text_title.text = guiMedia.title
text_desc.text = media.shortDesc text_desc.text = guiMedia.shortDesc
println("media streams: $streams") // specific gui
if (streamMedia.type == MediaType.TVSHOW) {
val episodes = streamMedia.streams.mapIndexed { index, _ ->
"${guiMedia.title} - Ep. ${index + 1}"
}
adapterEpisodes = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, episodes)
list_episodes.adapter = adapterEpisodes
} else if (streamMedia.type == MediaType.MOVIE) {
list_episodes.visibility = View.GONE
}
println("media streams: ${streamMedia.streams}")
initActions() initActions()
} }
private fun initActions() { private fun initActions() {
button_play.setOnClickListener { onClickButtonPlay() } button_play.setOnClickListener {
onClickButtonPlay()
}
list_episodes.setOnItemClickListener { _, _, position, _ ->
playStream(streamMedia.streams[position])
}
} }
private fun onClickButtonPlay() { private fun onClickButtonPlay() {
println("play ${streams.first()}") when (streamMedia.type) {
MediaType.MOVIE -> playStream(streamMedia.streams.first())
MediaType.TVSHOW -> playStream(streamMedia.streams.first())
MediaType.OTHER -> Log.e(javaClass.name, "Wrong Type, please report this issue.")
}
}
private fun playStream(url: String) {
val mainActivity = activity as MainActivity val mainActivity = activity as MainActivity
mainActivity.startPlayer(streams.first()) mainActivity.startPlayer(url)
} }
} }

View File

@ -5,8 +5,12 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.afollestad.materialdialogs.MaterialDialog
import kotlinx.android.synthetic.main.fragment_account.* import kotlinx.android.synthetic.main.fragment_account.*
import org.mosad.teapod.BuildConfig
import org.mosad.teapod.R import org.mosad.teapod.R
import org.mosad.teapod.preferences.EncryptedPreferences
import org.mosad.teapod.ui.components.LoginDialog
class AccountFragment : Fragment() { class AccountFragment : Fragment() {
@ -17,6 +21,27 @@ class AccountFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
text_account.text = "This is the Account Fragment" text_account_login.text = EncryptedPreferences.login
text_info_about_desc.text = getString(R.string.info_about_desc, BuildConfig.VERSION_NAME, getString(R.string.build_time))
initActions()
}
private fun initActions() {
linear_account_login.setOnClickListener {
LoginDialog(requireContext()).positiveButton {
EncryptedPreferences.saveCredentials(login, password, context)
}.show {
login = EncryptedPreferences.login
password = ""
}
}
linear_about.setOnClickListener {
MaterialDialog(requireContext())
.title(R.string.info_about)
.message(R.string.info_about_dialog)
.show()
}
} }
} }

View File

@ -25,8 +25,6 @@ package org.mosad.teapod.ui.components
import android.content.Context import android.content.Context
import android.widget.EditText import android.widget.EditText
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.WhichButton
import com.afollestad.materialdialogs.actions.getActionButton
import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.bottomsheets.setPeekHeight import com.afollestad.materialdialogs.bottomsheets.setPeekHeight
import com.afollestad.materialdialogs.customview.customView import com.afollestad.materialdialogs.customview.customView
@ -40,7 +38,7 @@ class LoginDialog(val context: Context) {
private val editTextLogin: EditText private val editTextLogin: EditText
private val editTextPassword: EditText private val editTextPassword: EditText
var email = "" var login = ""
var password = "" var password = ""
init { init {
@ -61,7 +59,7 @@ class LoginDialog(val context: Context) {
fun positiveButton(func: LoginDialog.() -> Unit): LoginDialog = apply { fun positiveButton(func: LoginDialog.() -> Unit): LoginDialog = apply {
dialog.positiveButton { dialog.positiveButton {
email = editTextLogin.text.toString() login = editTextLogin.text.toString()
password = editTextPassword.text.toString() password = editTextPassword.text.toString()
func() func()
@ -81,7 +79,7 @@ class LoginDialog(val context: Context) {
fun show(func: LoginDialog.() -> Unit): LoginDialog = apply { fun show(func: LoginDialog.() -> Unit): LoginDialog = apply {
func() func()
editTextLogin.setText(email) editTextLogin.setText(login)
editTextPassword.setText(password) editTextPassword.setText(password)
show() show()

View File

@ -19,7 +19,5 @@ class HomeFragment : Fragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
text_home.text = "This is the home fragment" text_home.text = "This is the home fragment"
println("HomeFragment created")
} }
} }

View File

@ -48,10 +48,8 @@ class SearchFragment : Fragment() {
} }
override fun onQueryTextChange(newText: String?): Boolean { override fun onQueryTextChange(newText: String?): Boolean {
println("new text is: $newText")
adapter.filter.filter(newText) adapter.filter.filter(newText)
adapter.notifyDataSetChanged() adapter.notifyDataSetChanged()
return false return false
} }
}) })

View File

@ -20,10 +20,6 @@ class CustomAdapter(val context: Context, private val originalMedia: ArrayList<G
private var filteredMedia = originalMedia.map { it.copy() } private var filteredMedia = originalMedia.map { it.copy() }
private val customFilter = CustomFilter() private val customFilter = CustomFilter()
init {
println("initial filtered size is: ${filteredMedia.size}")
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.linear_media, parent, false) val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.linear_media, parent, false)
@ -68,8 +64,6 @@ class CustomAdapter(val context: Context, private val originalMedia: ArrayList<G
results.values = filteredList results.values = filteredList
results.count = filteredList.size results.count = filteredList.size
println("filtered size is: ${results.count}")
return results return results
} }

View File

@ -1,7 +1,17 @@
package org.mosad.teapod.util package org.mosad.teapod.util
class DataTypes {
enum class MediaType {
OTHER,
MOVIE,
TVSHOW
}
}
data class GUIMedia(val title: String, val posterLink: String, val shortDesc : String, val link: String) { data class GUIMedia(val title: String, val posterLink: String, val shortDesc : String, val link: String) {
override fun toString(): String { override fun toString(): String {
return title return title
} }
} }
data class StreamMedia(val type: DataTypes.MediaType, val streams: ArrayList<String> = arrayListOf())

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
</vector>

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M8,5v14l11,-7z"/>
</vector>

View File

@ -4,20 +4,144 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#fafafa" android:background="#f5f5f5"
tools:context=".ui.account.AccountFragment"> tools:context=".ui.account.AccountFragment">
<TextView <ScrollView
android:id="@+id/text_account"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent">
android:layout_marginStart="8dp"
android:layout_marginTop="8dp" <LinearLayout
android:layout_marginEnd="8dp" android:layout_width="match_parent"
android:textAlignment="center" android:layout_height="wrap_content"
android:textSize="20sp" android:orientation="vertical">
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" <LinearLayout
app:layout_constraintStart_toStartOf="parent" android:id="@+id/linear_account"
app:layout_constraintTop_toTopOf="parent" /> android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="12dp"
android:background="#ffffff"
android:orientation="vertical">
<TextView
android:id="@+id/text_account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="7dp"
android:paddingEnd="7dp"
android:text="@string/account"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/linear_account_login"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/account"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="5dp"
android:scaleType="fitXY"
android:src="@drawable/ic_baseline_account_box_24"
app:srcCompat="@drawable/ic_baseline_account_box_24" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text_account_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/account_login_ex"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp" />
<TextView
android:id="@+id/text_account_login_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/account_login_desc"
android:textColor="@android:color/secondary_text_light" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/linear_info"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="12dp"
android:background="#ffffff"
android:orientation="vertical">
<TextView
android:id="@+id/text_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="7dp"
android:paddingEnd="7dp"
android:text="@string/info"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp"
android:textStyle="bold" />
<LinearLayout
android:id="@+id/linear_about"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="48dp"
android:minHeight="48dp"
android:padding="5dp"
android:scaleType="fitXY"
app:srcCompat="@drawable/ic_baseline_info_24" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/text_info_about"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/info_about"
android:textColor="@android:color/primary_text_light"
android:textSize="16sp" />
<TextView
android:id="@+id/text_info_about_desc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/info_about_desc"
android:textColor="@android:color/secondary_text_light" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#fafafa" android:background="#f5f5f5"
tools:context=".ui.home.HomeFragment"> tools:context=".ui.home.HomeFragment">
<TextView <TextView

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#fafafa" android:background="#f5f5f5"
tools:context=".ui.library.LibraryFragment"> tools:context=".ui.library.LibraryFragment">
<ListView <ListView

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#fafafa" android:background="#f5f5f5"
tools:context=".ui.MediaFragment"> tools:context=".ui.MediaFragment">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
@ -24,11 +24,21 @@
<Button <Button
android:id="@+id/button_play" android:id="@+id/button_play"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="48dp" android:layout_height="wrap_content"
android:layout_marginStart="7dp" android:layout_marginStart="7dp"
android:layout_marginTop="25dp" android:layout_marginTop="24dp"
android:layout_marginEnd="7dp" android:layout_marginEnd="7dp"
android:text="Play" android:background="#4A4141"
android:drawableStart="@drawable/ic_baseline_play_arrow_24"
android:drawablePadding="10dp"
android:drawableTint="#FFFFFF"
android:gravity="left|center_vertical"
android:paddingStart="160dp"
android:paddingEnd="160dp"
android:text="@string/button_play"
android:textAllCaps="false"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/image_poster" /> app:layout_constraintTop_toBottomOf="@+id/image_poster" />
@ -57,5 +67,13 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_title" /> app:layout_constraintTop_toBottomOf="@+id/text_title" />
<ListView
android:id="@+id/list_episodes"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="17dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text_desc" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout> </FrameLayout>

View File

@ -4,7 +4,7 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="#fafafa" android:background="#f5f5f5"
tools:context=".ui.search.SearchFragment"> tools:context=".ui.search.SearchFragment">
<SearchView <SearchView

View File

@ -8,6 +8,18 @@
<!-- search fragment --> <!-- search fragment -->
<string name="search_hint">Search for movies and series</string> <string name="search_hint">Search for movies and series</string>
<!-- media fragment -->
<string name="button_play">Play</string>
<!-- settings fragment -->
<string name="account">Account</string>
<string name="account_login_ex">user@example.com</string>
<string name="account_login_desc">Tap to edit</string>
<string name="info">Info</string>
<string name="info_about">Teapod by @Seil0</string>
<string name="info_about_desc" translatable="false">Version %1$s (%2$s)</string>
<string name="info_about_dialog" translatable="false">This software is published under the terms and conditions of GPL 3. For further information visit git.mosad.xyz/Seil0 \n\n© 2020 seil0@mosad.xyz</string>
<!-- dialogs --> <!-- dialogs -->
<string name="save">save</string> <string name="save">save</string>
<string name="cancel">@android:string/cancel</string> <string name="cancel">@android:string/cancel</string>