Super rudimentary support for stories
This commit is contained in:
parent
6876b1c449
commit
888c6328d9
|
@ -131,14 +131,14 @@ android {
|
|||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.2'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.3'
|
||||
|
||||
/**
|
||||
* AndroidX dependencies:
|
||||
*/
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
||||
implementation 'androidx.core:core-ktx:1.9.0'
|
||||
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||
implementation 'androidx.core:core-ktx:1.10.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
|
||||
|
@ -156,8 +156,8 @@ dependencies {
|
|||
implementation "androidx.lifecycle:lifecycle-common-java8:2.6.1"
|
||||
implementation "androidx.annotation:annotation:1.6.0"
|
||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||
implementation "androidx.activity:activity-ktx:1.7.0"
|
||||
implementation 'androidx.fragment:fragment-ktx:1.5.6'
|
||||
implementation "androidx.activity:activity-ktx:1.7.1"
|
||||
implementation 'androidx.fragment:fragment-ktx:1.5.7'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.8.1'
|
||||
implementation 'androidx.media2:media2-widget:1.2.1'
|
||||
implementation 'androidx.media2:media2-player:1.2.1'
|
||||
|
|
|
@ -64,6 +64,9 @@
|
|||
android:name=".posts.ReportActivity"
|
||||
android:screenOrientation="sensorPortrait"
|
||||
tools:ignore="LockedOrientationActivity" />
|
||||
|
||||
<activity
|
||||
android:name=".stories.StoriesActivity" />
|
||||
<activity
|
||||
android:name=".postCreation.PostCreationActivity"
|
||||
android:exported="true"
|
||||
|
|
|
@ -50,6 +50,7 @@ import org.pixeldroid.app.posts.feeds.cachedFeeds.postFeeds.PostFeedFragment
|
|||
import org.pixeldroid.app.profile.ProfileActivity
|
||||
import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment
|
||||
import org.pixeldroid.app.settings.SettingsActivity
|
||||
import org.pixeldroid.app.stories.StoriesActivity
|
||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
||||
import org.pixeldroid.app.utils.api.objects.Notification
|
||||
import org.pixeldroid.app.utils.db.addUser
|
||||
|
@ -229,12 +230,18 @@ class MainActivity : BaseThemedWithoutBarActivity() {
|
|||
primaryDrawerItem {
|
||||
nameRes = R.string.logout
|
||||
iconicsIcon = GoogleMaterial.Icon.gmd_close
|
||||
})
|
||||
},
|
||||
primaryDrawerItem {
|
||||
nameRes = R.string.story_image
|
||||
iconicsIcon = GoogleMaterial.Icon.gmd_auto_stories
|
||||
},
|
||||
)
|
||||
binding.drawer.onDrawerItemClickListener = { v, drawerItem, position ->
|
||||
when (position){
|
||||
1 -> launchActivity(ProfileActivity())
|
||||
2 -> launchActivity(SettingsActivity())
|
||||
3 -> logOut()
|
||||
4 -> launchActivity(StoriesActivity())
|
||||
}
|
||||
false
|
||||
}
|
||||
|
|
|
@ -195,8 +195,10 @@ class PostCreationViewModel(
|
|||
* and display it.
|
||||
*/
|
||||
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||
cursor.moveToFirst()
|
||||
cursor.getLong(sizeIndex)
|
||||
if(sizeIndex >= 0) {
|
||||
cursor.moveToFirst()
|
||||
cursor.getLong(sizeIndex)
|
||||
} else null
|
||||
} ?: 0
|
||||
} else {
|
||||
uri.toFile().length()
|
||||
|
|
|
@ -130,7 +130,7 @@ fun parseHTMLText(
|
|||
}
|
||||
|
||||
|
||||
fun setTextViewFromISO8601(date: Instant, textView: TextView, absoluteTime: Boolean, context: Context) {
|
||||
fun setTextViewFromISO8601(date: Instant, textView: TextView, absoluteTime: Boolean) {
|
||||
val now = Date.from(Instant.now()).time
|
||||
|
||||
try {
|
||||
|
@ -140,7 +140,7 @@ fun setTextViewFromISO8601(date: Instant, textView: TextView, absoluteTime: Bool
|
|||
android.text.format.DateUtils.SECOND_IN_MILLIS,
|
||||
android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
||||
|
||||
textView.text = if(absoluteTime) context.getString(R.string.posted_on).format(date)
|
||||
textView.text = if(absoluteTime) textView.context.getString(R.string.posted_on).format(date)
|
||||
else formattedDate
|
||||
|
||||
} catch (e: ParseException) {
|
||||
|
|
|
@ -139,8 +139,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||
setTextViewFromISO8601(
|
||||
status?.created_at!!,
|
||||
binding.postDate,
|
||||
isActivity,
|
||||
binding.root.context
|
||||
isActivity
|
||||
)
|
||||
|
||||
binding.postDomain.text = status?.getStatusDomain(domain, binding.postDomain.context)
|
||||
|
|
|
@ -221,8 +221,7 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||
setTextViewFromISO8601(
|
||||
it,
|
||||
notificationTime,
|
||||
false,
|
||||
itemView.context
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
package org.pixeldroid.app.stories
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DataSource
|
||||
import com.bumptech.glide.load.engine.GlideException
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import kotlinx.coroutines.launch
|
||||
import org.pixeldroid.app.databinding.ActivityStoriesBinding
|
||||
import org.pixeldroid.app.posts.setTextViewFromISO8601
|
||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
||||
|
||||
|
||||
class StoriesActivity: BaseThemedWithoutBarActivity() {
|
||||
|
||||
private lateinit var binding: ActivityStoriesBinding
|
||||
|
||||
private lateinit var model: StoriesViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityStoriesBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val _model: StoriesViewModel by viewModels {
|
||||
StoriesViewModelFactory(application)
|
||||
}
|
||||
model = _model
|
||||
|
||||
lifecycleScope.launch {
|
||||
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
model.uiState.collect { uiState ->
|
||||
binding.pause.isSelected = uiState.paused
|
||||
|
||||
uiState.age?.let { setTextViewFromISO8601(it, binding.storyAge, false) }
|
||||
|
||||
uiState.profilePicture?.let {
|
||||
Glide.with(binding.storyAuthorProfilePicture)
|
||||
.load(it)
|
||||
.apply(RequestOptions.circleCropTransform())
|
||||
.into(binding.storyAuthorProfilePicture)
|
||||
}
|
||||
|
||||
binding.storyAuthor.text = uiState.username
|
||||
|
||||
uiState.imageList.getOrNull(uiState.currentImage)?.let {
|
||||
Glide.with(binding.storyImage)
|
||||
.load(it)
|
||||
.listener(object : RequestListener<Drawable> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any?,
|
||||
target: Target<Drawable>?,
|
||||
isFirstResource: Boolean,
|
||||
): Boolean = false
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable?,
|
||||
m: Any?,
|
||||
target: Target<Drawable>?,
|
||||
dataSource: DataSource?,
|
||||
isFirstResource: Boolean,
|
||||
): Boolean {
|
||||
model.imageLoaded()
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(binding.storyImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model.count.observe(this) { state ->
|
||||
// Render state in UI
|
||||
model.uiState.value.durationList.getOrNull(model.uiState.value.currentImage)?.let {
|
||||
val percent = 100 - ((state/it.toFloat())*100).toInt()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
binding.progressBarStory.setProgress(percent, true)
|
||||
} else {
|
||||
binding.progressBarStory.progress = percent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.pause.setOnClickListener {
|
||||
//Set the button's appearance
|
||||
it.isSelected = !it.isSelected
|
||||
if (it.isSelected) {
|
||||
//Handle selected state change
|
||||
} else {
|
||||
//Handle de-select state change
|
||||
}
|
||||
}
|
||||
|
||||
binding.storyImage.setOnClickListener {
|
||||
model.pause()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
package org.pixeldroid.app.stories
|
||||
|
||||
import android.app.Application
|
||||
import android.os.CountDownTimer
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.pixeldroid.app.utils.PixelDroidApplication
|
||||
import org.pixeldroid.app.utils.api.objects.StoryCarousel
|
||||
import org.pixeldroid.app.utils.di.PixelfedAPIHolder
|
||||
import java.time.Instant
|
||||
import javax.inject.Inject
|
||||
|
||||
data class StoriesUiState(
|
||||
val profilePicture: String? = null,
|
||||
val username: String? = null,
|
||||
val age: Instant? = null,
|
||||
val currentImage: Int = 0,
|
||||
val imageList: List<String> = emptyList(),
|
||||
val durationList: List<Int> = emptyList(),
|
||||
val paused: Boolean = false,
|
||||
val errorMessage: String? = null,
|
||||
)
|
||||
|
||||
class StoriesViewModel(
|
||||
application: Application,
|
||||
) : AndroidViewModel(application) {
|
||||
|
||||
@Inject
|
||||
lateinit var apiHolder: PixelfedAPIHolder
|
||||
|
||||
private val _uiState: MutableStateFlow<StoriesUiState> = MutableStateFlow(StoriesUiState())
|
||||
|
||||
val uiState: StateFlow<StoriesUiState> = _uiState
|
||||
|
||||
var carousel: StoryCarousel? = null
|
||||
|
||||
val count = MutableLiveData<Long>()
|
||||
|
||||
private var timer: CountDownTimer? = null
|
||||
|
||||
init {
|
||||
(application as PixelDroidApplication).getAppComponent().inject(this)
|
||||
loadStories()
|
||||
}
|
||||
|
||||
private fun setTimer(timerLength: Long) {
|
||||
count.value = timerLength
|
||||
timer = object: CountDownTimer(timerLength * 1000, 500){
|
||||
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
count.value = millisUntilFinished / 1000
|
||||
Log.e("Timer second", "${count.value}")
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
goToNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun goToNext(){
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
currentImage = currentUiState.currentImage + 1,
|
||||
//TODO don't just take the first here, choose from activity input somehow?
|
||||
age = carousel?.nodes?.firstOrNull()?.nodes?.getOrNull(currentUiState.currentImage + 1)?.created_at
|
||||
)
|
||||
}
|
||||
//TODO when done with viewing all stories, close activity and move to profile (?)
|
||||
timer?.cancel()
|
||||
startTimerForCurrent()
|
||||
}
|
||||
|
||||
private fun loadStories() {
|
||||
viewModelScope.launch {
|
||||
try{
|
||||
val api = apiHolder.api ?: apiHolder.setToCurrentUser()
|
||||
carousel = api.carousel()
|
||||
|
||||
//TODO don't just take the first here, choose from activity input somehow?
|
||||
val chosenAccount = carousel?.nodes?.firstOrNull()
|
||||
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(
|
||||
profilePicture = chosenAccount?.user?.avatar,
|
||||
age = chosenAccount?.nodes?.getOrNull(0)?.created_at,
|
||||
username = chosenAccount?.user?.username, //TODO check if not username_acct, think about falling back on other option?
|
||||
errorMessage = null,
|
||||
currentImage = 0,
|
||||
imageList = chosenAccount?.nodes?.mapNotNull { it?.src } ?: emptyList(),
|
||||
durationList = chosenAccount?.nodes?.mapNotNull { it?.duration } ?: emptyList()
|
||||
)
|
||||
}
|
||||
startTimerForCurrent()
|
||||
} catch (exception: Exception){
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(errorMessage = "Something went wrong fetching the carousel")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startTimerForCurrent(){
|
||||
uiState.value.let {
|
||||
it.durationList.getOrNull(it.currentImage)?.toLong()?.let { time ->
|
||||
setTimer(time)
|
||||
timer?.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun imageLoaded() {/*
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(currentImage = currentUiState.currentImage + 1)
|
||||
}*/
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
if(_uiState.value.paused){
|
||||
timer?.start()
|
||||
} else {
|
||||
timer?.cancel()
|
||||
count.value?.let { setTimer(it) }
|
||||
}
|
||||
_uiState.update { currentUiState ->
|
||||
currentUiState.copy(paused = !currentUiState.paused)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StoriesViewModelFactory(val application: Application) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return modelClass.getConstructor(Application::class.java).newInstance(application)
|
||||
}
|
||||
}
|
|
@ -231,6 +231,20 @@ interface PixelfedAPI {
|
|||
@Query("post_id") post_id: String,
|
||||
)
|
||||
|
||||
@GET("/api/v1.1/stories/carousel")
|
||||
suspend fun carousel(
|
||||
): StoryCarousel
|
||||
|
||||
@POST("/api/v1.1/stories/seen")
|
||||
suspend fun carouselSeen(
|
||||
@Query("id") id: String //TODO figure out if this is the id of post or of user?
|
||||
)
|
||||
|
||||
@POST("/api/v1.1/stories/self-expire/{id}")
|
||||
suspend fun deleteCarousel(
|
||||
@Path("id") storyId: String
|
||||
)
|
||||
|
||||
//Used in our case to retrieve comments for a given status
|
||||
@GET("/api/v1/statuses/{id}/context")
|
||||
suspend fun statusComments(
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
package org.pixeldroid.app.utils.api.objects
|
||||
|
||||
import java.time.Instant
|
||||
|
||||
data class StoryCarousel(
|
||||
val self: CarouselUserContainer?,
|
||||
val nodes: List<CarouselUserContainer?>?
|
||||
)
|
||||
|
||||
data class CarouselUser(
|
||||
val id: String?,
|
||||
val username: String?,
|
||||
val username_acct: String?,
|
||||
val avatar: String?, // URL to account avatar
|
||||
val local: Boolean?, // Is this story from the local instance?
|
||||
val is_author: Boolean?, // Is this me? (seems redundant with id)
|
||||
)
|
||||
|
||||
/**
|
||||
* Container with a description of the [user] and a list of stories ([nodes])
|
||||
*/
|
||||
data class CarouselUserContainer(
|
||||
val user: CarouselUser?,
|
||||
val nodes: List<Story?>?,
|
||||
)
|
||||
|
||||
data class Story(
|
||||
val id: String?,
|
||||
val pid: String?, // id of author
|
||||
val type: String?, //TODO make enum of this? examples: "photo", ???
|
||||
val src: String?, // URL to photo of story
|
||||
val duration: Int?, //Time in seconds that the Story should be shown
|
||||
val seen: Boolean?, //Indication of whether this story has been seen. Set to true using carouselSeen
|
||||
val created_at: Instant?, //ISO 8601 Datetime
|
||||
)
|
|
@ -9,6 +9,7 @@ import org.pixeldroid.app.utils.BaseFragment
|
|||
import dagger.Component
|
||||
import org.pixeldroid.app.postCreation.PostCreationViewModel
|
||||
import org.pixeldroid.app.profile.EditProfileViewModel
|
||||
import org.pixeldroid.app.stories.StoriesViewModel
|
||||
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -22,6 +23,7 @@ interface ApplicationComponent {
|
|||
fun inject(notificationsWorker: NotificationsWorker)
|
||||
fun inject(postCreationViewModel: PostCreationViewModel)
|
||||
fun inject(editProfileViewModel: EditProfileViewModel)
|
||||
fun inject(storiesViewModel: StoriesViewModel)
|
||||
|
||||
val context: Context?
|
||||
val application: Application?
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
|
||||
</vector>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:drawable="@drawable/play"
|
||||
android:state_selected="true" />
|
||||
<item
|
||||
android:drawable="@drawable/pause"/>
|
||||
</selector>
|
|
@ -0,0 +1,79 @@
|
|||
<?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:background="@color/black"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/storyImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:contentDescription="@string/story_image"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/progressBarStory"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:srcCompat="@tools:sample/backgrounds/scenic[10]" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/pause"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="12dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/play_video"
|
||||
android:src="@drawable/play_pause"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/storyAuthorProfilePicture"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/storyAuthorProfilePicture"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBarStory"
|
||||
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||
android:progress="0"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/storyAuthorProfilePicture"
|
||||
android:layout_margin="12dp"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/progressBarStory"
|
||||
tools:srcCompat="@tools:sample/avatars"
|
||||
android:contentDescription="@string/profile_picture" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/storyAuthor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/storyAuthorProfilePicture"
|
||||
app:layout_constraintStart_toEndOf="@+id/storyAuthorProfilePicture"
|
||||
app:layout_constraintTop_toTopOf="@+id/storyAuthorProfilePicture"
|
||||
tools:text="username" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/storyAge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/storyAuthorProfilePicture"
|
||||
app:layout_constraintStart_toEndOf="@+id/storyAuthor"
|
||||
app:layout_constraintTop_toTopOf="@+id/storyAuthorProfilePicture"
|
||||
app:layout_constraintVertical_bias="0.517"
|
||||
tools:text="48m" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -330,4 +330,5 @@ For more info about Pixelfed, you can check here: https://pixelfed.org"</string>
|
|||
<string name="contains_nsfw">Contains NSFW media</string>
|
||||
<string name="switch_accounts">Switch accounts</string>
|
||||
<string name="summary_always_show_nsfw">NSFW/CW posts will not be blurred, and will be shown by default.</string>
|
||||
<string name="story_image">Story image</string>
|
||||
</resources>
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue