add status details
This commit is contained in:
parent
47afa8818f
commit
c5def59621
|
@ -48,6 +48,7 @@
|
|||
<activity android:name=".components.about.licenses.LicenseActivity"/>
|
||||
<activity android:name="at.connyduck.pixelcat.components.compose.ComposeActivity" />
|
||||
<activity android:name=".components.profile.ProfileActivity" />
|
||||
<activity android:name=".components.timeline.detail.DetailActivity" />
|
||||
|
||||
<service android:name=".components.compose.SendStatusService" />
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import androidx.paging.ExperimentalPagingApi
|
|||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||
import at.connyduck.pixelcat.R
|
||||
import at.connyduck.pixelcat.components.timeline.detail.DetailActivity
|
||||
import at.connyduck.pixelcat.components.util.extension.getDisplayWidthInPx
|
||||
import at.connyduck.pixelcat.components.util.getColorForAttr
|
||||
import at.connyduck.pixelcat.dagger.ViewModelFactory
|
||||
|
@ -101,4 +102,8 @@ class TimelineFragment : DaggerFragment(R.layout.fragment_timeline), TimeLineAct
|
|||
override fun onMediaVisibilityChanged(status: StatusEntity) {
|
||||
viewModel.onMediaVisibilityChanged(status)
|
||||
}
|
||||
|
||||
override fun onDetailsOpened(status: StatusEntity) {
|
||||
startActivity(DetailActivity.newIntent(requireContext(), status.actionableId))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import at.connyduck.pixelcat.databinding.ItemStatusBinding
|
|||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
import coil.api.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import java.text.DateFormat
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
interface TimeLineActionListener {
|
||||
|
@ -38,6 +39,7 @@ interface TimeLineActionListener {
|
|||
fun onBoost(post: StatusEntity)
|
||||
fun onReply(status: StatusEntity)
|
||||
fun onMediaVisibilityChanged(status: StatusEntity)
|
||||
fun onDetailsOpened(status: StatusEntity)
|
||||
}
|
||||
|
||||
object TimelineDiffUtil : DiffUtil.ItemCallback<StatusEntity>() {
|
||||
|
@ -66,65 +68,71 @@ class TimelineListAdapter(
|
|||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemStatusBinding>, position: Int) {
|
||||
|
||||
getItem(position)?.let { status ->
|
||||
|
||||
// TODO order the stuff here
|
||||
|
||||
(holder.binding.postImages.adapter as TimelineImageAdapter).images = status.attachments
|
||||
|
||||
val maxImageRatio = status.attachments.map {
|
||||
if(it.meta?.small?.width == null || it.meta.small.height == null) {
|
||||
1f
|
||||
} else {
|
||||
it.meta.small.height.toFloat() / it.meta.small.width.toFloat()
|
||||
}
|
||||
}.max()?.coerceAtMost(1f) ?: 1f
|
||||
|
||||
holder.binding.postImages.layoutParams.height = (displayWidth * maxImageRatio).toInt()
|
||||
|
||||
holder.binding.postAvatar.load(status.account.avatar) {
|
||||
transformations(RoundedCornersTransformation(25f))
|
||||
}
|
||||
|
||||
holder.binding.postAvatar.setOnClickListener {
|
||||
holder.binding.root.context.startActivity(ProfileActivity.newIntent(holder.binding.root.context, status.account.id))
|
||||
}
|
||||
|
||||
holder.binding.postDisplayName.text = status.account.displayName
|
||||
holder.binding.postName.text = "@${status.account.username}"
|
||||
|
||||
holder.binding.postLikeButton.isChecked = status.favourited
|
||||
|
||||
holder.binding.postLikeButton.setEventListener { _, _ ->
|
||||
listener.onFavorite(status)
|
||||
true
|
||||
}
|
||||
|
||||
holder.binding.postBoostButton.isChecked = status.reblogged
|
||||
|
||||
holder.binding.postBoostButton.setEventListener { _, _ ->
|
||||
listener.onBoost(status)
|
||||
true
|
||||
}
|
||||
|
||||
holder.binding.postReplyButton.setOnClickListener {
|
||||
listener.onReply(status)
|
||||
}
|
||||
|
||||
holder.binding.postIndicator.visible = status.attachments.size > 1
|
||||
|
||||
holder.binding.postImages.visible = status.attachments.isNotEmpty()
|
||||
|
||||
holder.binding.postDescription.text = status.content.parseAsHtml().trim()
|
||||
|
||||
holder.binding.postDate.text = dateTimeFormatter.format(status.createdAt)
|
||||
|
||||
holder.binding.postSensitiveMediaOverlay.visible = status.attachments.isNotEmpty() && !status.mediaVisible
|
||||
|
||||
holder.binding.postSensitiveMediaOverlay.setOnClickListener {
|
||||
listener.onMediaVisibilityChanged(status)
|
||||
}
|
||||
holder.bind(status, displayWidth, listener, dateTimeFormatter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun BindingHolder<ItemStatusBinding>.bind(status: StatusEntity, displayWidth: Int, listener: TimeLineActionListener, dateTimeFormatter: DateFormat) {
|
||||
// TODO order the stuff here
|
||||
|
||||
(binding.postImages.adapter as TimelineImageAdapter).images = status.attachments
|
||||
|
||||
val maxImageRatio = status.attachments.map {
|
||||
if(it.meta?.small?.width == null || it.meta.small.height == null) {
|
||||
1f
|
||||
} else {
|
||||
it.meta.small.height.toFloat() / it.meta.small.width.toFloat()
|
||||
}
|
||||
}.max()?.coerceAtMost(1f) ?: 1f
|
||||
|
||||
binding.postImages.layoutParams.height = (displayWidth * maxImageRatio).toInt()
|
||||
|
||||
binding.postAvatar.load(status.account.avatar) {
|
||||
transformations(RoundedCornersTransformation(25f))
|
||||
}
|
||||
|
||||
binding.postAvatar.setOnClickListener {
|
||||
binding.root.context.startActivity(ProfileActivity.newIntent(binding.root.context, status.account.id))
|
||||
}
|
||||
|
||||
binding.postDisplayName.text = status.account.displayName
|
||||
binding.postName.text = "@${status.account.username}"
|
||||
|
||||
binding.postLikeButton.isChecked = status.favourited
|
||||
|
||||
binding.postLikeButton.setEventListener { _, _ ->
|
||||
listener.onFavorite(status)
|
||||
true
|
||||
}
|
||||
|
||||
binding.postBoostButton.isChecked = status.reblogged
|
||||
|
||||
binding.postBoostButton.setEventListener { _, _ ->
|
||||
listener.onBoost(status)
|
||||
true
|
||||
}
|
||||
|
||||
binding.postReplyButton.setOnClickListener {
|
||||
listener.onReply(status)
|
||||
}
|
||||
|
||||
binding.postIndicator.visible = status.attachments.size > 1
|
||||
|
||||
binding.postImages.visible = status.attachments.isNotEmpty()
|
||||
|
||||
binding.postDescription.text = status.content.parseAsHtml().trim()
|
||||
|
||||
binding.postDate.text = dateTimeFormatter.format(status.createdAt)
|
||||
|
||||
binding.postSensitiveMediaOverlay.visible = status.attachments.isNotEmpty() && !status.mediaVisible
|
||||
|
||||
binding.postSensitiveMediaOverlay.setOnClickListener {
|
||||
listener.onMediaVisibilityChanged(status)
|
||||
}
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
listener.onDetailsOpened(status)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package at.connyduck.pixelcat.components.timeline.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.MergeAdapter
|
||||
import at.connyduck.pixelcat.components.general.BaseActivity
|
||||
import at.connyduck.pixelcat.components.timeline.TimeLineActionListener
|
||||
import at.connyduck.pixelcat.components.util.Success
|
||||
import at.connyduck.pixelcat.components.util.extension.getDisplayWidthInPx
|
||||
import at.connyduck.pixelcat.components.util.extension.hide
|
||||
import at.connyduck.pixelcat.components.util.extension.show
|
||||
import at.connyduck.pixelcat.dagger.ViewModelFactory
|
||||
import at.connyduck.pixelcat.databinding.ActivityDetailBinding
|
||||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
import at.connyduck.pixelcat.util.viewBinding
|
||||
import javax.inject.Inject
|
||||
|
||||
class DetailActivity: BaseActivity(), TimeLineActionListener {
|
||||
|
||||
@Inject
|
||||
lateinit var viewModelFactory: ViewModelFactory
|
||||
|
||||
private val viewModel: DetailViewModel by viewModels { viewModelFactory }
|
||||
|
||||
private val binding by viewBinding(ActivityDetailBinding::inflate)
|
||||
|
||||
private lateinit var statusAdapter: DetailStatusAdapter
|
||||
|
||||
private lateinit var repliesAdapter: DetailReplyAdapter
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
||||
binding.root.setOnApplyWindowInsetsListener { _, insets ->
|
||||
val top = insets.systemWindowInsetTop
|
||||
|
||||
binding.root.setPadding(0, top, 0, 0)
|
||||
|
||||
insets.consumeSystemWindowInsets()
|
||||
}
|
||||
|
||||
viewModel.setStatusId(intent.getStringExtra(EXTRA_STATUS_ID)!!)
|
||||
|
||||
val displayWidth = getDisplayWidthInPx()
|
||||
|
||||
statusAdapter = DetailStatusAdapter(displayWidth, this)
|
||||
repliesAdapter = DetailReplyAdapter(this)
|
||||
|
||||
binding.detailRecyclerView.adapter = MergeAdapter(statusAdapter, repliesAdapter)
|
||||
|
||||
viewModel.currentStatus.observe(this, Observer {
|
||||
if(it is Success) {
|
||||
binding.detailProgress.hide()
|
||||
binding.detailRecyclerView.show()
|
||||
statusAdapter.submitList(listOf(it.data))
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.replies.observe(this, Observer {
|
||||
if(it is Success) {
|
||||
repliesAdapter.submitList(it.data)
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
override fun onFavorite(post: StatusEntity) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onBoost(post: StatusEntity) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onReply(status: StatusEntity) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onMediaVisibilityChanged(status: StatusEntity) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onDetailsOpened(status: StatusEntity) {
|
||||
// nothing to do, we already are in details
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val EXTRA_STATUS_ID = "STATUS_ID"
|
||||
|
||||
fun newIntent(context: Context, statusId: String): Intent {
|
||||
return Intent(context, DetailActivity::class.java).apply {
|
||||
putExtra(EXTRA_STATUS_ID, statusId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
package at.connyduck.pixelcat.components.timeline.detail
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import at.connyduck.pixelcat.components.timeline.TimeLineActionListener
|
||||
import at.connyduck.pixelcat.components.timeline.TimelineDiffUtil
|
||||
import at.connyduck.pixelcat.components.util.BindingHolder
|
||||
import at.connyduck.pixelcat.databinding.ItemReplyBinding
|
||||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
import coil.api.load
|
||||
import coil.transform.RoundedCornersTransformation
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
class DetailReplyAdapter(
|
||||
private val listener: TimeLineActionListener
|
||||
): ListAdapter<StatusEntity, BindingHolder<ItemReplyBinding>>(TimelineDiffUtil) {
|
||||
|
||||
private val dateTimeFormatter = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT)
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): BindingHolder<ItemReplyBinding> {
|
||||
val binding = ItemReplyBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return BindingHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemReplyBinding>, position: Int) {
|
||||
getItem(position)?.let { status ->
|
||||
|
||||
holder.binding.postAvatar.load(status.account.avatar) {
|
||||
transformations(RoundedCornersTransformation(25f))
|
||||
}
|
||||
|
||||
holder.binding.postDisplayName.text = status.account.displayName
|
||||
holder.binding.postName.text = "@${status.account.username}"
|
||||
|
||||
holder.binding.postDescription.text = status.content.parseAsHtml().trim()
|
||||
|
||||
holder.binding.postDate.text = dateTimeFormatter.format(status.createdAt)
|
||||
|
||||
holder.binding.postLikeButton.isChecked = status.favourited
|
||||
|
||||
holder.binding.postLikeButton.setEventListener { _, _ ->
|
||||
listener.onFavorite(status)
|
||||
true
|
||||
}
|
||||
|
||||
holder.binding.postReplyButton.setOnClickListener {
|
||||
listener.onReply(status)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package at.connyduck.pixelcat.components.timeline.detail
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import at.connyduck.pixelcat.components.timeline.TimeLineActionListener
|
||||
import at.connyduck.pixelcat.components.timeline.TimelineDiffUtil
|
||||
import at.connyduck.pixelcat.components.timeline.TimelineImageAdapter
|
||||
import at.connyduck.pixelcat.components.timeline.bind
|
||||
import at.connyduck.pixelcat.components.util.BindingHolder
|
||||
import at.connyduck.pixelcat.databinding.ItemStatusBinding
|
||||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
|
||||
class DetailStatusAdapter(
|
||||
private val displayWidth: Int,
|
||||
private val listener: TimeLineActionListener
|
||||
): ListAdapter<StatusEntity, BindingHolder<ItemStatusBinding>>(TimelineDiffUtil) {
|
||||
|
||||
private val dateTimeFormatter = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT)
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int
|
||||
): BindingHolder<ItemStatusBinding> {
|
||||
val binding = ItemStatusBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
binding.postImages.adapter = TimelineImageAdapter()
|
||||
binding.postIndicator.setViewPager(binding.postImages)
|
||||
(binding.postImages.adapter as TimelineImageAdapter).registerAdapterDataObserver(binding.postIndicator.adapterDataObserver)
|
||||
return BindingHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: BindingHolder<ItemStatusBinding>, position: Int) {
|
||||
getItem(position)?.let { status ->
|
||||
holder.bind(status, displayWidth, listener, dateTimeFormatter)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* Copyright (C) 2020 Conny Duck
|
||||
*
|
||||
* This file is part of Pixelcat.
|
||||
*
|
||||
* Pixelcat is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Pixelcat is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package at.connyduck.pixelcat.components.timeline.detail
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.connyduck.pixelcat.components.util.Loading
|
||||
import at.connyduck.pixelcat.components.util.Success
|
||||
import at.connyduck.pixelcat.components.util.UiState
|
||||
import at.connyduck.pixelcat.components.util.Error
|
||||
import at.connyduck.pixelcat.db.AccountManager
|
||||
import at.connyduck.pixelcat.db.AppDatabase
|
||||
import at.connyduck.pixelcat.db.entitity.StatusEntity
|
||||
import at.connyduck.pixelcat.db.entitity.toEntity
|
||||
import at.connyduck.pixelcat.network.FediverseApi
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class DetailViewModel @Inject constructor(
|
||||
val api: FediverseApi,
|
||||
val db: AppDatabase,
|
||||
val accountManager: AccountManager
|
||||
) : ViewModel() {
|
||||
|
||||
val currentStatus = MutableLiveData<UiState<StatusEntity>>()
|
||||
val replies = MutableLiveData<UiState<List<StatusEntity>>>()
|
||||
|
||||
private var statusId = ""
|
||||
|
||||
fun setStatusId(statusId: String) {
|
||||
this.statusId = statusId
|
||||
|
||||
currentStatus.value = Loading()
|
||||
replies.value = Loading()
|
||||
|
||||
viewModelScope.launch {
|
||||
db.statusDao().status(statusId, accountManager.activeAccount()?.id!!)?.let {
|
||||
currentStatus.value = Success(it)
|
||||
}
|
||||
loadStatus()
|
||||
}
|
||||
viewModelScope.launch {
|
||||
loadReplies()
|
||||
}
|
||||
}
|
||||
|
||||
fun reload() {
|
||||
currentStatus.value = Loading()
|
||||
replies.value = Loading()
|
||||
viewModelScope.launch {
|
||||
loadStatus()
|
||||
}
|
||||
viewModelScope.launch {
|
||||
loadReplies()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private suspend fun loadStatus() {
|
||||
api.status(statusId).fold({
|
||||
val statusEntity = it.toEntity(accountManager.activeAccount()?.id!!)
|
||||
db.statusDao().insertOrReplace(statusEntity)
|
||||
currentStatus.value = Success(statusEntity)
|
||||
}, {
|
||||
currentStatus.value = Error(cause = it)
|
||||
})
|
||||
}
|
||||
|
||||
private suspend fun loadReplies() {
|
||||
api.statusContext(statusId).fold({
|
||||
replies.value = Success(it.descendants.map{
|
||||
descendant -> descendant.toEntity(accountManager.activeAccount()?.id!!)
|
||||
})
|
||||
}, {
|
||||
replies.value = Error(cause = it)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
|
@ -23,6 +23,7 @@ import at.connyduck.pixelcat.components.main.MainActivity
|
|||
import at.connyduck.pixelcat.components.about.AboutActivity
|
||||
import at.connyduck.pixelcat.components.about.licenses.LicenseActivity
|
||||
import at.connyduck.pixelcat.components.compose.ComposeActivity
|
||||
import at.connyduck.pixelcat.components.timeline.detail.DetailActivity
|
||||
import at.connyduck.pixelcat.components.login.LoginActivity
|
||||
import at.connyduck.pixelcat.components.profile.ProfileActivity
|
||||
import at.connyduck.pixelcat.components.settings.SettingsActivity
|
||||
|
@ -57,4 +58,7 @@ abstract class ActivityModule {
|
|||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesComposeActivity(): ComposeActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun contributesDetailActivity(): DetailActivity
|
||||
}
|
||||
|
|
|
@ -17,13 +17,12 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// from https://proandroiddev.com/viewmodel-with-dagger2-architecture-components-2e06f06c9455
|
||||
|
||||
package at.connyduck.pixelcat.dagger
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import at.connyduck.pixelcat.components.compose.ComposeViewModel
|
||||
import at.connyduck.pixelcat.components.timeline.detail.DetailViewModel
|
||||
import at.connyduck.pixelcat.components.login.LoginViewModel
|
||||
import at.connyduck.pixelcat.components.main.MainViewModel
|
||||
import at.connyduck.pixelcat.components.notifications.NotificationsViewModel
|
||||
|
@ -90,5 +89,10 @@ abstract class ViewModelModule {
|
|||
@IntoMap
|
||||
@ViewModelKey(ComposeViewModel::class)
|
||||
internal abstract fun composeViewModel(viewModel: ComposeViewModel): ViewModel
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ViewModelKey(DetailViewModel::class)
|
||||
internal abstract fun detailViewModel(viewModel: DetailViewModel): ViewModel
|
||||
// Add more ViewModels here
|
||||
}
|
||||
|
|
|
@ -39,6 +39,9 @@ interface TimelineDao {
|
|||
@Delete
|
||||
suspend fun delete(status: StatusEntity)
|
||||
|
||||
@Query("SELECT * FROM StatusEntity WHERE id = :statusId AND accountId = :accountId")
|
||||
suspend fun status(statusId: String, accountId: Long): StatusEntity?
|
||||
|
||||
@Query("SELECT * FROM StatusEntity WHERE accountId = :accountId ORDER BY LENGTH(id) DESC, id DESC")
|
||||
fun statuses(accountId: Long): PagingSource<Int, StatusEntity>
|
||||
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package at.connyduck.pixelcat.model
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class StatusContext (
|
||||
val ancestors: List<Status>,
|
||||
val descendants: List<Status>
|
||||
)
|
|
@ -19,13 +19,7 @@
|
|||
|
||||
package at.connyduck.pixelcat.network
|
||||
|
||||
import at.connyduck.pixelcat.model.AccessToken
|
||||
import at.connyduck.pixelcat.model.Account
|
||||
import at.connyduck.pixelcat.model.AppCredentials
|
||||
import at.connyduck.pixelcat.model.Attachment
|
||||
import at.connyduck.pixelcat.model.NewStatus
|
||||
import at.connyduck.pixelcat.model.Relationship
|
||||
import at.connyduck.pixelcat.model.Status
|
||||
import at.connyduck.pixelcat.model.*
|
||||
import at.connyduck.pixelcat.network.calladapter.NetworkResponse
|
||||
import okhttp3.MultipartBody
|
||||
import retrofit2.http.Body
|
||||
|
@ -183,4 +177,14 @@ interface FediverseApi {
|
|||
suspend fun unreblogStatus(
|
||||
@Path("id") statusId: String
|
||||
): NetworkResponse<Status>
|
||||
|
||||
@GET("api/v1/statuses/{id}")
|
||||
suspend fun status(
|
||||
@Path("id") statusId: String
|
||||
): NetworkResponse<Status>
|
||||
|
||||
@GET("api/v1/statuses/{id}/context")
|
||||
suspend fun statusContext(
|
||||
@Path("id") statusId: String
|
||||
): NetworkResponse<StatusContext>
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@drawable/pixelcat_gradient">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/detailAppbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/detailToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_scrollFlags="scroll|enterAlways"
|
||||
app:navigationIcon="@drawable/ic_cat_small"
|
||||
app:title="@string/app_name"
|
||||
app:titleTextAppearance="@style/TextAppearanceToolbar"
|
||||
app:titleTextColor="?attr/colorPrimary" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/detailSwipeRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:attr/windowBackground"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/detailRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/detailProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
|
@ -0,0 +1,102 @@
|
|||
<?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:sparkbutton="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipChildren="false"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/postAvatar"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:importantForAccessibility="no"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:background="#f00" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/postDisplayName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toStartOf="@id/postName"
|
||||
app:layout_constraintStart_toEndOf="@id/postAvatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Conny Duck" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/postName"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:ellipsize="end"
|
||||
android:lines="1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/postDisplayName"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="\@connyduck\@chaos.social" />
|
||||
|
||||
<androidx.emoji.widget.EmojiTextView
|
||||
android:id="@+id/postDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/postAvatar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/postDisplayName"
|
||||
tools:text="This is the caption" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/postDate"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@+id/postAvatar"
|
||||
app:layout_constraintTop_toBottomOf="@id/postDescription"
|
||||
tools:text="4 hours ago" />
|
||||
|
||||
<at.connyduck.sparkbutton.SparkButton
|
||||
android:id="@+id/postLikeButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:layout_constraintStart_toEndOf="@id/postDate"
|
||||
app:layout_constraintTop_toBottomOf="@id/postDescription"
|
||||
sparkbutton:activeImage="@drawable/ic_heart_filled"
|
||||
sparkbutton:iconSize="24dp"
|
||||
sparkbutton:inactiveImage="@drawable/ic_heart"
|
||||
sparkbutton:primaryColor="@color/heart_button_primary"
|
||||
sparkbutton:secondaryColor="@color/heart_button_secondary" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/postReplyButton"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="8dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/ic_message"
|
||||
app:layout_constraintStart_toEndOf="@id/postLikeButton"
|
||||
app:layout_constraintTop_toBottomOf="@id/postDescription" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue