add sensitive media overlay

This commit is contained in:
Konrad Pozniak 2020-06-13 19:55:36 +02:00
parent 0e8ad1e9ed
commit 4984a68750
7 changed files with 169 additions and 143 deletions

View File

@ -46,7 +46,7 @@ class TimelineFragment : DaggerFragment(R.layout.fragment_timeline), TimeLineAct
binding.timelineRecyclerView.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL)) binding.timelineRecyclerView.addItemDecoration(DividerItemDecoration(requireContext(), DividerItemDecoration.VERTICAL))
lifecycleScope.launch { lifecycleScope.launch {
viewModel.flow.collectLatest { pagingData -> viewModel.statusFlow.collectLatest { pagingData ->
adapter.submitData(pagingData) adapter.submitData(pagingData)
} }
} }
@ -77,4 +77,8 @@ class TimelineFragment : DaggerFragment(R.layout.fragment_timeline), TimeLineAct
override fun onReply(status: StatusEntity) { override fun onReply(status: StatusEntity) {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override fun onMediaVisibilityChanged(status: StatusEntity) {
viewModel.onMediaVisibilityChanged(status)
}
} }

View File

@ -18,6 +18,7 @@ interface TimeLineActionListener {
fun onFavorite(post: StatusEntity) fun onFavorite(post: StatusEntity)
fun onBoost(post: StatusEntity) fun onBoost(post: StatusEntity)
fun onReply(status: StatusEntity) fun onReply(status: StatusEntity)
fun onMediaVisibilityChanged(status: StatusEntity)
} }
object TimelineDiffUtil : DiffUtil.ItemCallback<StatusEntity>() { object TimelineDiffUtil : DiffUtil.ItemCallback<StatusEntity>() {
@ -82,6 +83,12 @@ class TimelineListAdapter(
holder.binding.postDescription.text = status.content.parseAsHtml().trim() holder.binding.postDescription.text = status.content.parseAsHtml().trim()
holder.binding.postDate.text = dateTimeFormatter.format(status.createdAt) holder.binding.postDate.text = dateTimeFormatter.format(status.createdAt)
holder.binding.postSensitiveMediaOverlay.visible = status.attachments.isNotEmpty() && !status.mediaVisible
holder.binding.postSensitiveMediaOverlay.setOnClickListener {
listener.onMediaVisibilityChanged(status)
}
} }
} }
} }

View File

@ -1,6 +1,5 @@
package at.connyduck.pixelcat.components.timeline package at.connyduck.pixelcat.components.timeline
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.paging.ExperimentalPagingApi import androidx.paging.ExperimentalPagingApi
@ -11,37 +10,39 @@ import at.connyduck.pixelcat.db.AccountManager
import at.connyduck.pixelcat.db.AppDatabase import at.connyduck.pixelcat.db.AppDatabase
import at.connyduck.pixelcat.db.entitity.StatusEntity import at.connyduck.pixelcat.db.entitity.StatusEntity
import at.connyduck.pixelcat.network.FediverseApi import at.connyduck.pixelcat.network.FediverseApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class TimelineViewModel @Inject constructor( class TimelineViewModel @Inject constructor(
// private val repository: TimelineRepo,
private val accountManager: AccountManager, private val accountManager: AccountManager,
private val db: AppDatabase, private val db: AppDatabase,
fediverseApi: FediverseApi fediverseApi: FediverseApi
) : ViewModel() { ) : ViewModel() {
private val accountId = MutableLiveData<Long>() @OptIn(FlowPreview::class)
init {
viewModelScope.launch {
val currentAccountId = accountManager.activeAccount()?.id!!
accountId.postValue(currentAccountId)
}
}
@ExperimentalPagingApi @ExperimentalPagingApi
val flow = Pager( val statusFlow = accountManager::activeAccount.asFlow()
// Configure how data is loaded by passing additional properties to .flatMapConcat { activeAccount ->
// PagingConfig, such as prefetchDistance. Pager(
config = PagingConfig(pageSize = 10, enablePlaceholders = false), config = PagingConfig(pageSize = 10, enablePlaceholders = false),
remoteMediator = TimelineRemoteMediator(0, fediverseApi, db), remoteMediator = TimelineRemoteMediator(activeAccount?.id!!, fediverseApi, db),
pagingSourceFactory = { db.statusDao().statuses(0) } pagingSourceFactory = { db.statusDao().statuses(activeAccount.id) }
).flow.cachedIn(viewModelScope) ).flow
}
.cachedIn(viewModelScope)
fun onFavorite(status: StatusEntity) { fun onFavorite(status: StatusEntity) {
viewModelScope.launch { viewModelScope.launch {
// repository.onFavorite(status, accountManager.activeAccount()?.id!!) // repository.onFavorite(status, accountManager.activeAccount()?.id!!)
} }
} }
fun onMediaVisibilityChanged(status: StatusEntity) {
viewModelScope.launch {
db.statusDao().changeMediaVisibility(!status.mediaVisible, status.id, accountManager.activeAccount()?.id!!)
}
}
} }

View File

@ -25,4 +25,7 @@ interface TimelineDao {
@Query("DELETE FROM StatusEntity WHERE accountId = :accountId") @Query("DELETE FROM StatusEntity WHERE accountId = :accountId")
suspend fun clearAll(accountId: Long) suspend fun clearAll(accountId: Long)
@Query("UPDATE StatusEntity SET mediaVisible = :visible WHERE id = :statusId AND accountId = :accountId")
suspend fun changeMediaVisibility(visible: Boolean, statusId: String, accountId: Long)
} }

View File

@ -32,7 +32,7 @@ data class StatusEntity(
val mediaVisible: Boolean val mediaVisible: Boolean
) )
fun Status.toEntity(accountId: Long, mediaPosition: Int = 0, mediaVisible: Boolean = this.sensitive) = StatusEntity( fun Status.toEntity(accountId: Long, mediaPosition: Int = 0, mediaVisible: Boolean = !this.sensitive) = StatusEntity(
accountId = accountId, accountId = accountId,
id = id, id = id,
url = actionableStatus.url, url = actionableStatus.url,

View File

@ -1,137 +1,147 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:sparkbutton="http://schemas.android.com/apk/res-auto" xmlns:sparkbutton="http://schemas.android.com/apk/res-auto"
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="wrap_content" android:layout_height="wrap_content"
android:paddingBottom="8dp"> android:paddingBottom="8dp">
<ImageView <ImageView
android:id="@+id/postAvatar" android:id="@+id/postAvatar"
android:layout_width="50dp" android:layout_width="50dp"
android:layout_height="50dp" android:layout_height="50dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:importantForAccessibility="no" android:importantForAccessibility="no"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:background="#f00" /> tools:background="#f00" />
<androidx.emoji.widget.EmojiTextView <androidx.emoji.widget.EmojiTextView
android:id="@+id/postDisplayName" android:id="@+id/postDisplayName"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@id/postName" app:layout_constraintBottom_toTopOf="@id/postName"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/postAvatar" app:layout_constraintStart_toEndOf="@id/postAvatar"
app:layout_constraintTop_toTopOf="@id/postAvatar" app:layout_constraintTop_toTopOf="@id/postAvatar"
tools:text="Conny Duck" /> tools:text="Conny Duck" />
<TextView <TextView
android:id="@+id/postName" android:id="@+id/postName"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:ellipsize="end" android:ellipsize="end"
android:lines="1" android:lines="1"
app:layout_constraintBottom_toBottomOf="@id/postAvatar" app:layout_constraintBottom_toBottomOf="@id/postAvatar"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/postAvatar" app:layout_constraintStart_toEndOf="@id/postAvatar"
app:layout_constraintTop_toBottomOf="@id/postDisplayName" app:layout_constraintTop_toBottomOf="@id/postDisplayName"
tools:text="\@connyduck\@chaos.social" /> tools:text="\@connyduck\@chaos.social" />
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/postImages" android:id="@+id/postImages"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="200dp" android:layout_height="200dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:background="#000" android:background="#000"
android:orientation="horizontal" android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layout_constraintTop_toBottomOf="@id/postAvatar" />
app:layout_constraintTop_toBottomOf="@id/postAvatar" />
<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_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/postImages"
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" />
<at.connyduck.sparkbutton.SparkButton
android:id="@+id/postBoostButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="8dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintStart_toEndOf="@id/postLikeButton"
app:layout_constraintTop_toBottomOf="@id/postImages"
sparkbutton:activeImage="@drawable/ic_repeat"
sparkbutton:iconSize="24dp"
sparkbutton:inactiveImage="@drawable/ic_repeat"
sparkbutton:primaryColor="@color/boost_button_primary"
sparkbutton:secondaryColor="@color/boost_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/postBoostButton"
app:layout_constraintTop_toBottomOf="@id/postImages" />
<me.relex.circleindicator.CircleIndicator3
android:id="@+id/postIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="104dp"
app:ci_drawable="@drawable/pager_indicator_dot"
app:ci_drawable_unselected="@drawable/pager_indicator_dot"
app:layout_constraintBottom_toBottomOf="@id/postReplyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/postReplyButton"
app:layout_constraintTop_toTopOf="@id/postReplyButton" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/postDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintTop_toBottomOf="@id/postLikeButton"
tools:text="This is the caption" />
<TextView <TextView
android:id="@+id/postDate" android:id="@+id/postSensitiveMediaOverlay"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="0dp"
android:layout_marginStart="16dp" android:background="#ccc"
android:layout_marginTop="8dp" android:gravity="center"
android:layout_marginEnd="16dp" android:textStyle="bold"
android:textSize="12sp" android:text="@string/sensitive_media"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintBottom_toBottomOf="@id/postImages"
app:layout_constraintTop_toBottomOf="@id/postDescription" app:layout_constraintTop_toTopOf="@id/postImages" />
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_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/postImages"
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" />
<at.connyduck.sparkbutton.SparkButton
android:id="@+id/postBoostButton"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="8dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintStart_toEndOf="@id/postLikeButton"
app:layout_constraintTop_toBottomOf="@id/postImages"
sparkbutton:activeImage="@drawable/ic_repeat"
sparkbutton:iconSize="24dp"
sparkbutton:inactiveImage="@drawable/ic_repeat"
sparkbutton:primaryColor="@color/boost_button_primary"
sparkbutton:secondaryColor="@color/boost_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/postBoostButton"
app:layout_constraintTop_toBottomOf="@id/postImages" />
<me.relex.circleindicator.CircleIndicator3
android:id="@+id/postIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="104dp"
app:ci_drawable="@drawable/pager_indicator_dot"
app:ci_drawable_unselected="@drawable/pager_indicator_dot"
app:layout_constraintBottom_toBottomOf="@id/postReplyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/postReplyButton"
app:layout_constraintTop_toTopOf="@id/postReplyButton" />
<androidx.emoji.widget.EmojiTextView
android:id="@+id/postDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
app:layout_constraintTop_toBottomOf="@id/postLikeButton"
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_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/postDescription"
tools:text="4 hours ago" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -74,5 +74,6 @@
<string name="navigation_menu">Menu</string> <string name="navigation_menu">Menu</string>
<string name="navigation_accounts">Accounts</string> <string name="navigation_accounts">Accounts</string>
<string name="action_add_new_account">Add Account</string> <string name="action_add_new_account">Add Account</string>
<string name="sensitive_media">Sensitive media</string>
</resources> </resources>