Super rudimentary support for stories
This commit is contained in:
parent
6876b1c449
commit
888c6328d9
@ -131,14 +131,14 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
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:
|
* AndroidX dependencies:
|
||||||
*/
|
*/
|
||||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||||
implementation 'androidx.core:core-splashscreen:1.0.0'
|
implementation 'androidx.core:core-splashscreen:1.0.1'
|
||||||
implementation 'androidx.core:core-ktx:1.9.0'
|
implementation 'androidx.core:core-ktx:1.10.0'
|
||||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||||
implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3'
|
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.lifecycle:lifecycle-common-java8:2.6.1"
|
||||||
implementation "androidx.annotation:annotation:1.6.0"
|
implementation "androidx.annotation:annotation:1.6.0"
|
||||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation "androidx.activity:activity-ktx:1.7.0"
|
implementation "androidx.activity:activity-ktx:1.7.1"
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.5.6'
|
implementation 'androidx.fragment:fragment-ktx:1.5.7'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.8.1'
|
implementation 'androidx.work:work-runtime-ktx:2.8.1'
|
||||||
implementation 'androidx.media2:media2-widget:1.2.1'
|
implementation 'androidx.media2:media2-widget:1.2.1'
|
||||||
implementation 'androidx.media2:media2-player:1.2.1'
|
implementation 'androidx.media2:media2-player:1.2.1'
|
||||||
|
@ -64,6 +64,9 @@
|
|||||||
android:name=".posts.ReportActivity"
|
android:name=".posts.ReportActivity"
|
||||||
android:screenOrientation="sensorPortrait"
|
android:screenOrientation="sensorPortrait"
|
||||||
tools:ignore="LockedOrientationActivity" />
|
tools:ignore="LockedOrientationActivity" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".stories.StoriesActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".postCreation.PostCreationActivity"
|
android:name=".postCreation.PostCreationActivity"
|
||||||
android:exported="true"
|
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.profile.ProfileActivity
|
||||||
import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment
|
import org.pixeldroid.app.searchDiscover.SearchDiscoverFragment
|
||||||
import org.pixeldroid.app.settings.SettingsActivity
|
import org.pixeldroid.app.settings.SettingsActivity
|
||||||
|
import org.pixeldroid.app.stories.StoriesActivity
|
||||||
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
import org.pixeldroid.app.utils.BaseThemedWithoutBarActivity
|
||||||
import org.pixeldroid.app.utils.api.objects.Notification
|
import org.pixeldroid.app.utils.api.objects.Notification
|
||||||
import org.pixeldroid.app.utils.db.addUser
|
import org.pixeldroid.app.utils.db.addUser
|
||||||
@ -229,12 +230,18 @@ class MainActivity : BaseThemedWithoutBarActivity() {
|
|||||||
primaryDrawerItem {
|
primaryDrawerItem {
|
||||||
nameRes = R.string.logout
|
nameRes = R.string.logout
|
||||||
iconicsIcon = GoogleMaterial.Icon.gmd_close
|
iconicsIcon = GoogleMaterial.Icon.gmd_close
|
||||||
})
|
},
|
||||||
|
primaryDrawerItem {
|
||||||
|
nameRes = R.string.story_image
|
||||||
|
iconicsIcon = GoogleMaterial.Icon.gmd_auto_stories
|
||||||
|
},
|
||||||
|
)
|
||||||
binding.drawer.onDrawerItemClickListener = { v, drawerItem, position ->
|
binding.drawer.onDrawerItemClickListener = { v, drawerItem, position ->
|
||||||
when (position){
|
when (position){
|
||||||
1 -> launchActivity(ProfileActivity())
|
1 -> launchActivity(ProfileActivity())
|
||||||
2 -> launchActivity(SettingsActivity())
|
2 -> launchActivity(SettingsActivity())
|
||||||
3 -> logOut()
|
3 -> logOut()
|
||||||
|
4 -> launchActivity(StoriesActivity())
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -195,8 +195,10 @@ class PostCreationViewModel(
|
|||||||
* and display it.
|
* and display it.
|
||||||
*/
|
*/
|
||||||
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
val sizeIndex = cursor.getColumnIndex(OpenableColumns.SIZE)
|
||||||
cursor.moveToFirst()
|
if(sizeIndex >= 0) {
|
||||||
cursor.getLong(sizeIndex)
|
cursor.moveToFirst()
|
||||||
|
cursor.getLong(sizeIndex)
|
||||||
|
} else null
|
||||||
} ?: 0
|
} ?: 0
|
||||||
} else {
|
} else {
|
||||||
uri.toFile().length()
|
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
|
val now = Date.from(Instant.now()).time
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -140,7 +140,7 @@ fun setTextViewFromISO8601(date: Instant, textView: TextView, absoluteTime: Bool
|
|||||||
android.text.format.DateUtils.SECOND_IN_MILLIS,
|
android.text.format.DateUtils.SECOND_IN_MILLIS,
|
||||||
android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE).toString()
|
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
|
else formattedDate
|
||||||
|
|
||||||
} catch (e: ParseException) {
|
} catch (e: ParseException) {
|
||||||
|
@ -139,8 +139,7 @@ class StatusViewHolder(val binding: PostFragmentBinding) : RecyclerView.ViewHold
|
|||||||
setTextViewFromISO8601(
|
setTextViewFromISO8601(
|
||||||
status?.created_at!!,
|
status?.created_at!!,
|
||||||
binding.postDate,
|
binding.postDate,
|
||||||
isActivity,
|
isActivity
|
||||||
binding.root.context
|
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.postDomain.text = status?.getStatusDomain(domain, binding.postDomain.context)
|
binding.postDomain.text = status?.getStatusDomain(domain, binding.postDomain.context)
|
||||||
|
@ -221,8 +221,7 @@ class NotificationsFragment : CachedFeedFragment<Notification>() {
|
|||||||
setTextViewFromISO8601(
|
setTextViewFromISO8601(
|
||||||
it,
|
it,
|
||||||
notificationTime,
|
notificationTime,
|
||||||
false,
|
false
|
||||||
itemView.context
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
110
app/src/main/java/org/pixeldroid/app/stories/StoriesActivity.kt
Normal file
110
app/src/main/java/org/pixeldroid/app/stories/StoriesActivity.kt
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
143
app/src/main/java/org/pixeldroid/app/stories/StoriesViewModel.kt
Normal file
143
app/src/main/java/org/pixeldroid/app/stories/StoriesViewModel.kt
Normal file
@ -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,
|
@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
|
//Used in our case to retrieve comments for a given status
|
||||||
@GET("/api/v1/statuses/{id}/context")
|
@GET("/api/v1/statuses/{id}/context")
|
||||||
suspend fun statusComments(
|
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 dagger.Component
|
||||||
import org.pixeldroid.app.postCreation.PostCreationViewModel
|
import org.pixeldroid.app.postCreation.PostCreationViewModel
|
||||||
import org.pixeldroid.app.profile.EditProfileViewModel
|
import org.pixeldroid.app.profile.EditProfileViewModel
|
||||||
|
import org.pixeldroid.app.stories.StoriesViewModel
|
||||||
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker
|
import org.pixeldroid.app.utils.notificationsWorker.NotificationsWorker
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ interface ApplicationComponent {
|
|||||||
fun inject(notificationsWorker: NotificationsWorker)
|
fun inject(notificationsWorker: NotificationsWorker)
|
||||||
fun inject(postCreationViewModel: PostCreationViewModel)
|
fun inject(postCreationViewModel: PostCreationViewModel)
|
||||||
fun inject(editProfileViewModel: EditProfileViewModel)
|
fun inject(editProfileViewModel: EditProfileViewModel)
|
||||||
|
fun inject(storiesViewModel: StoriesViewModel)
|
||||||
|
|
||||||
val context: Context?
|
val context: Context?
|
||||||
val application: Application?
|
val application: Application?
|
||||||
|
5
app/src/main/res/drawable/pause.xml
Normal file
5
app/src/main/res/drawable/pause.xml
Normal file
@ -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>
|
5
app/src/main/res/drawable/play.xml
Normal file
5
app/src/main/res/drawable/play.xml
Normal file
@ -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>
|
8
app/src/main/res/drawable/play_pause.xml
Normal file
8
app/src/main/res/drawable/play_pause.xml
Normal file
@ -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>
|
79
app/src/main/res/layout/activity_stories.xml
Normal file
79
app/src/main/res/layout/activity_stories.xml
Normal file
@ -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="contains_nsfw">Contains NSFW media</string>
|
||||||
<string name="switch_accounts">Switch accounts</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="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>
|
</resources>
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user