Merge branch 'account-activity' into 'master'

Account activity

See merge request agosto182/p2play!18
This commit is contained in:
Ivan Agosto 2024-06-05 18:27:38 +00:00
commit f86d66fdab
23 changed files with 793 additions and 18 deletions

View File

@ -45,6 +45,7 @@ dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'androidx.activity:activity:1.8.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
@ -53,4 +54,5 @@ dependencies {
implementation 'androidx.media3:media3-ui:1.3.1'
implementation 'androidx.media3:media3-exoplayer-hls:1.3.1'
implementation "androidx.media3:media3-session:1.3.1"
implementation 'com.google.code.gson:gson:2.8.8'
}

View File

@ -13,6 +13,11 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.P2play">
<activity
android:name=".AccountActivity"
android:exported="false"
android:theme="@style/Theme.P2play.NoActionBar" />
<activity
android:name=".SettingsActivity2"
android:exported="false"

View File

@ -0,0 +1,68 @@
package org.libre.agosto.p2play
import android.os.AsyncTask
import android.os.Bundle
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import com.google.android.material.tabs.TabLayoutMediator
import com.squareup.picasso.Picasso
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.libre.agosto.p2play.ajax.Accounts
import org.libre.agosto.p2play.databinding.ActivityAccountBinding
import org.libre.agosto.p2play.databinding.ActivityMainBinding
import org.libre.agosto.p2play.fragmentAdapters.AccountAdapter
import org.libre.agosto.p2play.models.AccountModel
class AccountActivity : AppCompatActivity() {
private lateinit var binding: ActivityAccountBinding
private val client = Accounts()
private lateinit var accountId: String
private lateinit var adapter: AccountAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAccountBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
accountId = this.intent.extras?.getString("accountId")!!
supportActionBar?.setDisplayHomeAsUpEnabled(true)
binding.toolbar.setNavigationOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
adapter = AccountAdapter(supportFragmentManager, lifecycle)
binding.viewpager.adapter = adapter
adapter.accountId = accountId
TabLayoutMediator(binding.tabs, binding.viewpager, false, false) { tab, position ->
tab.text = adapter.get(position)
}.attach()
}
override fun onResume() {
super.onResume()
getChannelInfo()
}
private fun getChannelInfo() {
CoroutineScope(Dispatchers.IO).launch {
val account = client.get(accountId)
withContext(Dispatchers.Main) {
binding.collapsingToolbar.title = account.displayName
adapter.account = account
if (account.avatars.size > 0) {
Picasso.get().load("https://${ManagerSingleton.url}${account.avatars.last().path}").into(binding.profileImage)
}
}
}
}
}

View File

@ -0,0 +1,130 @@
package org.libre.agosto.p2play.adapters
import android.content.Context
import android.content.Intent
import android.os.AsyncTask
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.TextView
import androidx.fragment.app.FragmentActivity
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import org.libre.agosto.p2play.AccountActivity
import org.libre.agosto.p2play.ChannelActivity
import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.R
import org.libre.agosto.p2play.ajax.Actions
import org.libre.agosto.p2play.models.ChannelModel
class ChannelAdapter(private val myDataset: ArrayList<ChannelModel>): RecyclerView.Adapter<ChannelAdapter.ViewHolder>() {
private val actionsService = Actions()
lateinit var parent: FragmentActivity
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {
// Define click listener for the ViewHolder's View
val channelImage: ImageView = view.findViewById(R.id.channelImage)
val channelName: TextView = view.findViewById(R.id.channelName)
val channelDescription: TextView = view.findViewById(R.id.channelDescription)
val subscribeButton: Button = view.findViewById(R.id.subscribeBtn)
val context: Context = view.context
var isSubscribed: Boolean = false
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChannelAdapter.ViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.view_channel, parent, false) as View
return ViewHolder(view)
}
override fun getItemCount() = myDataset.size
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.channelName.text = myDataset[position].name
holder.channelDescription.text = myDataset[position].description
if (myDataset[position].channelImg != "") {
Picasso.get().load("https://${ManagerSingleton.url}${myDataset[position].channelImg}").into(holder.channelImage)
} else {
holder.channelImage.setImageResource(R.drawable.default_avatar)
}
if (ManagerSingleton.user.status == 1) {
getSubscription(myDataset[position], holder)
} else {
holder.subscribeButton.visibility = View.GONE
}
holder.subscribeButton.setOnClickListener {
this.subscribeAction(myDataset[position], holder)
}
holder.channelImage.setOnClickListener {
this.launchChannelActivity(myDataset[position].getAccount())
}
holder.channelName.setOnClickListener {
this.launchChannelActivity(myDataset[position].getAccount())
}
}
private fun getSubscription(channel: ChannelModel, holder: ViewHolder) {
AsyncTask.execute {
holder.isSubscribed = actionsService.getSubscription(ManagerSingleton.token.token, channel.getAccount())
parent.runOnUiThread {
if (holder.isSubscribed) {
holder.subscribeButton.text = parent.getText(R.string.unSubscribeBtn)
} else {
holder.subscribeButton.text = parent.getText(R.string.subscribeBtn)
}
}
}
}
private fun subscribe(channel: ChannelModel, holder: ViewHolder) {
AsyncTask.execute {
val res = actionsService.subscribe(ManagerSingleton.token.token, channel.getAccount())
parent.runOnUiThread {
if (res == 1) {
holder.subscribeButton.text = parent.getString(R.string.unSubscribeBtn)
ManagerSingleton.toast(parent.getString(R.string.subscribeMsg), parent)
getSubscription(channel, holder)
} else {
ManagerSingleton.toast(parent.getString(R.string.errorMsg), parent)
}
}
}
}
private fun unSubscribe(channel: ChannelModel, holder: ViewHolder) {
AsyncTask.execute {
val res = actionsService.unSubscribe(ManagerSingleton.token.token, channel.getAccount())
parent.runOnUiThread {
if (res == 1) {
holder.subscribeButton.text = parent.getString(R.string.subscribeBtn)
ManagerSingleton.toast(parent.getString(R.string.unSubscribeMsg), parent)
getSubscription(channel, holder)
} else {
ManagerSingleton.toast(parent.getString(R.string.errorMsg), parent)
}
}
}
}
private fun subscribeAction(channel: ChannelModel, holder: ViewHolder) {
if (holder.isSubscribed) {
unSubscribe(channel, holder)
} else {
subscribe(channel, holder)
}
}
private fun launchChannelActivity (channelId: String) {
val intent = Intent(parent, ChannelActivity::class.java)
intent.putExtra("channel", channelId)
parent.startActivity(intent)
}
}

View File

@ -1,6 +1,7 @@
package org.libre.agosto.p2play.adapters
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.Html
import android.view.LayoutInflater
@ -12,6 +13,7 @@ import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import org.libre.agosto.p2play.AccountActivity
import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.R
import org.libre.agosto.p2play.dialogs.ThreadDialog
@ -86,11 +88,11 @@ class CommentariesAdapter(private val myDataset: ArrayList<CommentaryModel>) :
}
// TODO: Support for view and account (is different than a video channel)
// holder.userImg.setOnClickListener {
// val intent = Intent(holder.context, ChannelActivity::class.java)
// intent.putExtra("channel", myDataset[position].getAccount())
// holder.context.startActivity(intent)
// }
holder.userImg.setOnClickListener {
val intent = Intent(holder.context, AccountActivity::class.java)
intent.putExtra("accountId", myDataset[position].getAccount())
holder.context.startActivity(intent)
}
}
// Return the size of your dataset (invoked by the layout manager)

View File

@ -9,6 +9,7 @@ import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import org.libre.agosto.p2play.AccountActivity
import org.libre.agosto.p2play.ChannelActivity
import org.libre.agosto.p2play.ManagerSingleton
import org.libre.agosto.p2play.R

View File

@ -0,0 +1,58 @@
package org.libre.agosto.p2play.ajax
import android.util.JsonReader
import com.google.gson.Gson
import org.libre.agosto.p2play.models.AccountModel
import org.libre.agosto.p2play.models.ChannelModel
import org.libre.agosto.p2play.models.VideoModel
import java.io.InputStreamReader
class Accounts : Client() {
fun get(accountId: String): AccountModel {
val con = this.newCon("accounts/$accountId", "GET")
lateinit var account: AccountModel
try {
if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream)
account = Gson().fromJson(response, AccountModel::class.java)
}
} catch (err: Exception) {
err.printStackTrace()
}
return account
}
fun getChannels(accountId: String): ArrayList<ChannelModel> {
val con = this.newCon("accounts/$accountId/video-channels", "GET")
val channels = arrayListOf<ChannelModel>()
try {
if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream)
val data = JsonReader(response)
data.beginObject()
while (data.hasNext()) {
when (data.nextName()) {
"data" -> {
data.beginArray()
while (data.hasNext()) {
val channel = ChannelModel()
channel.parseChannel(data)
channels.add(channel)
}
data.endArray()
}
else -> data.skipValue()
}
}
data.endObject()
data.close()
}
} catch (err: Exception) {
err.printStackTrace()
}
return channels
}
}

View File

@ -6,14 +6,6 @@ import java.io.InputStreamReader
class Channels : Client() {
private fun parseChannel(data: JsonReader): ChannelModel {
val channel = ChannelModel()
data.close()
return channel
}
fun getChannelInfo(account: String): ChannelModel {
val con = this.newCon("video-channels/$account", "GET")
var channel = ChannelModel()

View File

@ -218,4 +218,24 @@ class Videos : Client() {
con.disconnect()
return video
}
fun accountVideos(account: String, start: Int): ArrayList<VideoModel> {
val count = ManagerSingleton.videosCount
val params = "start=$start&count=$count"
val con = this.newCon("accounts/$account/videos?$params", "GET")
var videos = arrayListOf<VideoModel>()
try {
if (con.responseCode == 200) {
val response = InputStreamReader(con.inputStream)
val data = JsonReader(response)
videos = parseVideos(data)
data.close()
}
} catch (err: Exception) {
err.printStackTrace()
}
con.disconnect()
return videos
}
}

View File

@ -0,0 +1,38 @@
package org.libre.agosto.p2play.fragmentAdapters
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import androidx.viewpager2.adapter.FragmentStateAdapter
import org.libre.agosto.p2play.fragments.AccountVideosFragment
import org.libre.agosto.p2play.fragments.AccountChannelsFragment
import org.libre.agosto.p2play.fragments.AccountInfoFragment
import org.libre.agosto.p2play.models.AccountModel
val TAB_NAMES = arrayOf(
"Videos",
"Channels",
"Info"
)
class AccountAdapter(fm: FragmentManager, c: Lifecycle) : FragmentStateAdapter(fm, c) {
lateinit var accountId: String
lateinit var account: AccountModel
override fun getItemCount(): Int = 3
override fun createFragment(i: Int): Fragment {
val fragment = when (i) {
0 -> AccountVideosFragment.newInstance(accountId)
1 -> AccountChannelsFragment.newInstance(accountId)
2 -> AccountInfoFragment.newInstance(account)
else -> throw Error("Invalid tab")
}
return fragment
}
fun get(position: Int): CharSequence {
return TAB_NAMES[position]
}
}

View File

@ -0,0 +1,82 @@
package org.libre.agosto.p2play.fragments
import android.os.AsyncTask
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.libre.agosto.p2play.adapters.ChannelAdapter
import org.libre.agosto.p2play.ajax.Accounts
import org.libre.agosto.p2play.databinding.FragmentChannelsBinding
import org.libre.agosto.p2play.helpers.getViewManager
import org.libre.agosto.p2play.models.ChannelModel
private const val ARG_PARAM1 = "accountId"
class AccountChannelsFragment : Fragment() {
private var _binding: FragmentChannelsBinding? = null
private val binding get() = _binding!!
private var accountId: String? = null
private val client = Accounts()
private lateinit var viewManager: RecyclerView.LayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
accountId = it.getString(ARG_PARAM1)
}
viewManager = getViewManager(this.requireContext(), resources)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentChannelsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onResume() {
super.onResume()
getChannels()
}
private fun getChannels() {
AsyncTask.execute {
val channels = client.getChannels(this.accountId!!)
requireActivity().runOnUiThread {
initRecycler(channels)
}
}
}
private fun initRecycler(data: ArrayList<ChannelModel>) {
val viewAdapter = ChannelAdapter(data)
binding.channelList.apply {
viewAdapter.parent = requireActivity()
setHasFixedSize(true)
// use a linear layout manager
layoutManager = viewManager
// specify an viewAdapter (see also next example)
adapter = viewAdapter
}
}
companion object {
@JvmStatic
fun newInstance(accountId: String) =
AccountChannelsFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, accountId)
}
}
}
}

View File

@ -0,0 +1,50 @@
package org.libre.agosto.p2play.fragments
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import org.libre.agosto.p2play.databinding.FragmentChannelInfoBinding
import org.libre.agosto.p2play.models.AccountModel
import java.io.Serializable
private const val ARG_PARAM1 = "account"
class AccountInfoFragment : Fragment() {
private var _binding: FragmentChannelInfoBinding? = null
private val binding get() = _binding!!
private var account: AccountModel? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
account = it.getSerializable(ARG_PARAM1) as AccountModel
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentChannelInfoBinding.inflate(inflater, container, false)
binding.account.text = account?.name
binding.host.text = account?.host
binding.description.text = account?.description
return binding.root
}
companion object {
@JvmStatic
fun newInstance(account: AccountModel) =
AccountInfoFragment().apply {
arguments = Bundle().apply {
val param = account as Serializable
putSerializable(ARG_PARAM1, param)
}
}
}
}

View File

@ -0,0 +1,86 @@
package org.libre.agosto.p2play.fragments
import android.os.AsyncTask
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.libre.agosto.p2play.adapters.VideosAdapter
import org.libre.agosto.p2play.ajax.Videos
import org.libre.agosto.p2play.databinding.FragmentChannelVideosBinding
import org.libre.agosto.p2play.helpers.getViewManager
import org.libre.agosto.p2play.models.VideoModel
private const val ARG_PARAM1 = "accountId"
class AccountVideosFragment : Fragment() {
private var _binding: FragmentChannelVideosBinding? = null
private val binding get() = _binding!!
private var accountId: String? = "agosto182"
private val videosService = Videos()
private lateinit var viewManager: RecyclerView.LayoutManager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
accountId = it.getString(ARG_PARAM1, accountId)
}
viewManager = getViewManager(this.requireContext(), resources)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentChannelVideosBinding.inflate(inflater, container, false)
return binding.root
}
override fun onResume() {
super.onResume()
getVideos()
}
private fun getVideos() {
AsyncTask.execute {
val videos = videosService.accountVideos(this.accountId!!, 0)
activity?.runOnUiThread {
initRecycler(videos)
}
}
}
private fun initRecycler(data: ArrayList<VideoModel>) {
// val data = arrayListOf<VideoModel>()
val viewAdapter = VideosAdapter(data)
binding.videosList.apply {
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
setHasFixedSize(true)
// use a linear layout manager
layoutManager = viewManager
// specify an viewAdapter (see also next example)
adapter = viewAdapter
}
}
companion object {
@JvmStatic
fun newInstance(param1: String) =
AccountVideosFragment().apply {
arguments = Bundle().apply {
putString(ARG_PARAM1, param1)
}
}
}
}

View File

@ -0,0 +1,16 @@
package org.libre.agosto.p2play.models
import java.io.Serializable
data class AccountAvatar (
val path: String
)
class AccountModel (
val name: String,
val url: String,
val host: String,
val avatars: ArrayList<AccountAvatar>,
val displayName: String,
val description: String
): Serializable

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_height="wrap_content"
android:layout_width="match_parent">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id="@+id/collapsingToolbar"
android:layout_width="match_parent"
android:layout_height="250dp"
android:fitsSystemWindows="true"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways"
app:layout_collapseMode="pin" />
<ImageView
android:id="@+id/backgroundImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
app:layout_collapseMode="pin"
android:orientation="vertical">
<ImageView
android:id="@+id/profileImage"
android:layout_width="80dp"
android:layout_height="80dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/default_avatar" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways"/>
</com.google.android.material.appbar.AppBarLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.AccountInfoFragment">
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:padding="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/accountTitle"
style="@style/TextAppearance.Material3.BodyMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/accountName" />
<TextView
android:id="@+id/account"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="" />
<androidx.legacy.widget.Space
android:layout_width="match_parent"
android:layout_height="8dp" />
<TextView
android:id="@+id/hostTitle"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/hostIndicator" />
<TextView
android:id="@+id/host"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="" />
<androidx.legacy.widget.Space
android:layout_width="match_parent"
android:layout_height="8dp" />
<TextView
android:id="@+id/descriptionTitle"
style="@style/TextAppearance.MaterialComponents.Body2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/descriptionTxt" />
<TextView
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="" />
<androidx.legacy.widget.Space
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</FrameLayout>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.AccountVideosFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/videosList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.AccountChannelsFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/channelList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@ -0,0 +1,53 @@
<?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">
<ImageView
android:id="@+id/channelImage"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:scaleType="fitCenter"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:srcCompat="@drawable/default_avatar" />
<TextView
android:id="@+id/channelName"
android:layout_width="200dp"
android:layout_height="26dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="TextView"
android:textAppearance="@style/TextAppearance.AppCompat.Title"
app:layout_constraintStart_toEndOf="@+id/channelImage"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/channelDescription"
android:layout_width="198dp"
android:layout_height="17dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="TextView"
app:layout_constraintStart_toEndOf="@+id/channelImage"
app:layout_constraintTop_toBottomOf="@+id/channelName" />
<Button
android:id="@+id/subscribeBtn"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/subscribeBtn"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2,7 +2,7 @@
<resources>
<!-- Start Global strings -->
<string name="comming">Proximamente</string>
<string name="charging">Cargando...</string>
<string name="charging">Cargando</string>
<!-- End Global strings -->
<!-- Start About strings -->

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="comming">In arrivo!</string>
<string name="charging">Caricamento...</string>
<string name="charging">Caricamento</string>
<string name="aboutLabel">Informazioni su P2Play</string>
<string name="aboutText">P2Play è un\'applicazione Android non ufficiale per PeerTube. Puoi visualizzare e contribuire al codice su GitLab:</string>
<string name="aboutStatus">Puoi seguire il nostro blog:</string>
@ -79,5 +79,4 @@
<string name="hostIndicator">Host:</string>
<string name="reportDialog">Motivo per la segnalazione di questo video:</string>
<string name="reportDialogMsg">Hai segnalato il video</string>
</resources>

View File

@ -117,12 +117,15 @@
<string name="pref_message_exit">Restart the app to apply changes</string>
<string name="pref_videos_count_title">Videos per page</string>
<!-- End Settings strings -->
<!-- Start Channel strings -->
<!-- Start Channel/Account strings -->
<string name="followersIndicator">Followers:</string>
<string name="hostIndicator">Host:</string>
<string name="accountName">Account Name:</string>
<!-- End Channel strings -->
<!-- Start Prompt string -->
<string name="reportDialog">Reason to report this video:</string>
<string name="reportDialogMsg">You reported the video</string>
<!-- TODO: Remove or change this placeholder text -->
<string name="hello_blank_fragment">Hello blank fragment</string>
<!-- End Prompt strings -->
</resources>

View File

@ -1,5 +1,5 @@
<resources>
<style name="Theme.P2play" parent="Theme.MaterialComponents.DayNight">
<item name="colorPrimary">@color/md_theme_light_primary</item>
<item name="colorOnPrimary">@color/md_theme_light_onPrimary</item>
@ -22,4 +22,8 @@
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="Theme.P2play.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="Theme.P2play.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>